diff --git a/Directory.Build.props b/Directory.Build.props index 4107aab1da083..3e494e19c3d9e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -65,7 +65,7 @@ - src/tasks/AotCompilerTask/MonoAOTCompiler.props - src/tasks/AppleAppBuilder/Xcode.cs - src/tasks/MobileBuildTasks/Apple/AppleProject.cs - - dotnet/installer repo > src/redist/targets/GeneratePKG.targets + - https://github.com/dotnet/sdk repo > src/Installer/redist-installer/targets/GeneratePKG.targets --> 21 12.2 diff --git a/README.md b/README.md index 1d3328b81b2d6..5f42a80256c42 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ all supported platforms, as well as the sources to .NET runtime and libraries. Official Starting Page: -* [How to use .NET](https://docs.microsoft.com/dotnet/core/get-started) (with VS, VS Code, command-line CLI) +* [How to use .NET](https://learn.microsoft.com/dotnet/core/get-started) (with VS, VS Code, command-line CLI) * [Install official releases](https://dotnet.microsoft.com/download) * [Install daily builds](docs/project/dogfooding.md) - * [Documentation](https://docs.microsoft.com/dotnet/core) (Get Started, Tutorials, Porting from .NET Framework, API reference, ...) - * [Deploying apps](https://docs.microsoft.com/dotnet/core/deploying) + * [Documentation](https://learn.microsoft.com/dotnet/core) (Get Started, Tutorials, Porting from .NET Framework, API reference, ...) + * [Deploying apps](https://learn.microsoft.com/dotnet/core/deploying) * [Supported OS versions](https://github.com/dotnet/core/blob/main/os-lifecycle-policy.md) * [Roadmap](https://github.com/dotnet/core/blob/main/roadmap.md) * [Releases](https://github.com/dotnet/core/tree/main/release-notes) @@ -53,7 +53,7 @@ For other issues, please file them to their appropriate sibling repos. We have l ## Useful Links * [.NET source index](https://source.dot.net) / [.NET Framework source index](https://referencesource.microsoft.com) -* [API Reference docs](https://docs.microsoft.com/dotnet/api) +* [API Reference docs](https://learn.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 * [.NET Discord Server](https://aka.ms/dotnet-discord) - a place to discuss the development of .NET and its ecosystem @@ -65,7 +65,7 @@ For other issues, please file them to their appropriate sibling repos. We have l There are many .NET related projects on GitHub. * [.NET home repo](https://github.com/Microsoft/dotnet) - links to 100s of .NET projects, from Microsoft and the community. -* [ASP.NET Core home](https://docs.microsoft.com/aspnet/core) - the best place to start learning about ASP.NET Core. +* [ASP.NET Core home](https://learn.microsoft.com/aspnet/core) - the best place to start learning about ASP.NET Core. This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org) to clarify expected behavior in our community. For more information, see the [.NET Foundation Code of Conduct](https://www.dotnetfoundation.org/code-of-conduct). diff --git a/docs/README.md b/docs/README.md index 46e859f945c6c..debc23ef00daf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ Getting Started =============== - [Installing the .NET SDK](https://dotnet.microsoft.com/download) -- [Official .NET Docs](https://docs.microsoft.com/dotnet/core/) +- [Official .NET Docs](https://learn.microsoft.com/dotnet/core/) Workflow (Building, testing, benchmarking, profiling, etc.) =============== @@ -69,8 +69,8 @@ Other Information - [.NET Glossary](project/glossary.md) - [.NET Filename Encyclopedia](project/dotnet-filenames.md) -- [Porting to .NET Core](https://docs.microsoft.com/en-us/dotnet/standard/analyzers/portability-analyzer) +- [Porting to .NET Core](https://learn.microsoft.com/dotnet/standard/analyzers/portability-analyzer) - [.NET Standards (Ecma)](project/dotnet-standards.md) - [CLR Configuration Knobs](../src/coreclr/inc/clrconfigvalues.h) -- [CLR overview](https://docs.microsoft.com/dotnet/standard/clr) +- [CLR overview](https://learn.microsoft.com/dotnet/standard/clr) - [Wikipedia Entry for the CLR](https://en.wikipedia.org/wiki/Common_Language_Runtime) diff --git a/docs/area-owners.md b/docs/area-owners.md index cbcfb5d6b2033..a06da33480c0d 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -27,7 +27,7 @@ Note: Editing this file doesn't update the mapping used by `@msftbot` for area-s | area-crossgen2-coreclr | @steveisok | @dotnet/crossgen-contrib | | | area-Debugger-mono | @tommcdon | @thaystg | | | area-DependencyModel | @ericstj | @dotnet/area-dependencymodel | Included: | -| area-Diagnostics-coreclr | @tommcdon | @tommcdon | | +| area-Diagnostics-coreclr | @tommcdon | @tommcdon @dotnet/dotnet-diag | | | area-Diagnostics-mono | @tommcdon | @tommcdon @mdh1418 @thaystg | | | area-EnC-mono | @tommcdon | @mikelle-rogers @thaystg | Hot Reload on WebAssembly, Android, iOS, etc . @lambdageek to consult | | area-ExceptionHandling-coreclr | @mangod9 | @janvorli | | diff --git a/docs/coding-guidelines/adding-api-guidelines.md b/docs/coding-guidelines/adding-api-guidelines.md index 2db8da8cc1f3e..f405468bac7a9 100644 --- a/docs/coding-guidelines/adding-api-guidelines.md +++ b/docs/coding-guidelines/adding-api-guidelines.md @@ -25,7 +25,7 @@ the implementation without compat concerns in future releases. ### Determine target framework `net8.0` is the target framework version currently under development and the new apis -should be added to `net8.0`. [More Information on TargetFrameworks](https://docs.microsoft.com/en-us/dotnet/standard/frameworks) +should be added to `net8.0`. [More Information on TargetFrameworks](https://learn.microsoft.com/dotnet/standard/frameworks) ## Making the changes in repo @@ -42,7 +42,7 @@ If your new API or the APIs it calls throw any exceptions, those need to be manu After your change is merged, we will eventually port them to the dotnet-api-docs repo, where we will review them for language and proper style (For more information, see the [API writing guidelines](https://github.com/dotnet/dotnet-api-docs/wiki)). -Once the dotnet-api-docs change is merged, your comments will start showing up in the official API documentation at http://docs.microsoft.com/, and later they'll appear in IntelliSense in Visual Studio and Visual Studio Code. +Once the dotnet-api-docs change is merged, your comments will start showing up in the official API documentation at https://learn.microsoft.com, and later they'll appear in IntelliSense in Visual Studio and Visual Studio Code. Once the documentation is official, any subsequent updates to it must be made directly in https://github.com/dotnet/dotnet-api-docs/. It's fine to make updates to the triple slash comments later, they just won't automatically flow into the official docs. ## FAQ diff --git a/docs/coding-guidelines/api-guidelines/README.md b/docs/coding-guidelines/api-guidelines/README.md index 31dd7181785b3..e2025c13cee98 100644 --- a/docs/coding-guidelines/api-guidelines/README.md +++ b/docs/coding-guidelines/api-guidelines/README.md @@ -9,5 +9,5 @@ actual [book][FDG]. To submit new proposals for design guidelines, simply create a PR adding or modifying an existing file. -[docs]: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/ +[docs]: https://learn.microsoft.com/dotnet/standard/design-guidelines/ [FDG]: https://amazon.com/dp/0135896460 diff --git a/docs/coding-guidelines/clr-code-guide.md b/docs/coding-guidelines/clr-code-guide.md index ef6f7a555d615..a5b90d7e237af 100644 --- a/docs/coding-guidelines/clr-code-guide.md +++ b/docs/coding-guidelines/clr-code-guide.md @@ -1028,14 +1028,14 @@ Here are some immediate tips for working well with the managed-debugging service - Do not change behavior when under the debugger. An app should behave identically when run outside or under the debugger. This is absolutely necessary else we get complaints like "my program only crashes when run under the debugger". This is also necessary because somebody may attach a debugger to an app after the fact. Specific examples of this: - Don't assume that just because an app is under the debugger that somebody is trying to debug it. - Don't add additional run-time error checks when under the debugger. For example, avoid code like: if ((IsDebuggerPresent() && (argument == null)) { throw MyException(); } - - Avoid massive perf changes when under the debugger. For example, don't use an interpreted stub just because you're under the debugger. We then get bugs like [my app is 100x slower when under a debugger](https://docs.microsoft.com/en-us/archive/blogs/jmstall/psa-pinvokes-may-be-100x-slower-under-the-debugger). + - Avoid massive perf changes when under the debugger. For example, don't use an interpreted stub just because you're under the debugger. We then get bugs like [my app is 100x slower when under a debugger](https://learn.microsoft.com/archive/blogs/jmstall/psa-pinvokes-may-be-100x-slower-under-the-debugger). - Avoid algorithmic changes. For example, do not make the JIT generate non-optimized code just because an app is under the debugger. Do not make the loader policy resolve to a debuggable-ngen image just because an app is under the debugger. - Separate your code into a) side-effect-free (non-mutating) read-only accessors and b) functions that change state. The motivation is that the debugger needs to be able to read-state in a non-invasive way. For example, don't just have GetFoo() that will lazily create a Foo if it's not available. Instead, split it out like so: - GetFoo() - fails if a Foo does not exist. Being non-mutating, this should also be GC_NOTRIGGER. Non-mutating will also make it much easier to DAC-ize. This is what the debugger will call. - and GetOrCreateFoo() that is built around GetFoo(). The rest of the runtime can call this. - The debugger can then just call GetFoo(), and deal with the failure accordingly. - If you add a new stub (or way to call managed code), make sure that you can source-level step-in (F11) it under the debugger. The debugger is not psychic. A source-level step-in needs to be able to go from the source-line before a call to the source-line after the call, or managed code developers will be very confused. If you make that call transition be a giant 500 line stub, you must cooperate with the debugger for it to know how to step-through it. (This is what StubManagers are all about. See [src\vm\stubmgr.h](https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/stubmgr.h)). Try doing a step-in through your new codepath under the debugger. -- **Beware of timeouts** : The debugger may completely suspend your process at arbitrary points. In most cases, the debugger will do the right thing (and suspend your timeout too), but not always. For example, if you have some other process waiting for info from the debuggee, it [may hit a timeout](https://docs.microsoft.com/en-us/archive/blogs/jmstall/why-you-sometimes-get-a-bogus-contextswitchdeadlock-mda-under-the-debugger). +- **Beware of timeouts** : The debugger may completely suspend your process at arbitrary points. In most cases, the debugger will do the right thing (and suspend your timeout too), but not always. For example, if you have some other process waiting for info from the debuggee, it [may hit a timeout](https://learn.microsoft.com/archive/blogs/jmstall/why-you-sometimes-get-a-bogus-contextswitchdeadlock-mda-under-the-debugger). - **Use CLR synchronization primitives (like Crst)**. In addition to all the reasons listed in the synchronization section, the CLR-aware primitives can cooperate with the debugging services. For example: - The debugger needs to know when threads are modifying sensitive data (which correlates to when the threads lock that data). - Timeouts for CLR synchronization primitives may operate better in the face of being debugged. diff --git a/docs/coding-guidelines/framework-design-guidelines-digest.md b/docs/coding-guidelines/framework-design-guidelines-digest.md index ff2ce139977c4..d48ecc60727dc 100644 --- a/docs/coding-guidelines/framework-design-guidelines-digest.md +++ b/docs/coding-guidelines/framework-design-guidelines-digest.md @@ -308,5 +308,5 @@ conformance to the [Framework Design Guidelines][FDG] (also see [MSDN](https://m ## Presentations -* [Overview of the Framework Design Guidelines](https://docs.microsoft.com/en-us/archive/blogs/kcwalina/online-lecture-on-api-design) -* [TechEd 2007 Presentation about framework engineering](https://docs.microsoft.com/en-us/archive/blogs/kcwalina/video-recording-of-framework-engineering-architecting-designing-and-developing-reusable-libraries) +* [Overview of the Framework Design Guidelines](https://learn.microsoft.com/archive/blogs/kcwalina/online-lecture-on-api-design) +* [TechEd 2007 Presentation about framework engineering](https://learn.microsoft.com/archive/blogs/kcwalina/video-recording-of-framework-engineering-architecting-designing-and-developing-reusable-libraries) diff --git a/docs/coding-guidelines/interop-guidelines.md b/docs/coding-guidelines/interop-guidelines.md index b755f3cd74ec7..8338f4c1d04e2 100644 --- a/docs/coding-guidelines/interop-guidelines.md +++ b/docs/coding-guidelines/interop-guidelines.md @@ -1,7 +1,7 @@ Interop Guidelines ================== -We follow the [best practices for native interop](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices) with the additional guidelines below that are specific to this repo. +We follow the [best practices for native interop](https://learn.microsoft.com/dotnet/standard/native-interop/best-practices) with the additional guidelines below that are specific to this repo. ## Goals We have the following goals related to interop code being used in dotnet/runtime: @@ -166,7 +166,7 @@ Using enums instead of partial, static classes can lead to needing lots of casts ## P/Invoke Definitions -When defining the P/Invoke signatures and structs, we follow the guidelines in the [interop best practices documentation](https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices). +When defining the P/Invoke signatures and structs, we follow the guidelines in the [interop best practices documentation](https://learn.microsoft.com/dotnet/standard/native-interop/best-practices). The runtime repo makes use of [source-generated p/invokes](../design/features/source-generator-pinvokes.md) whenever possible (see [the compatibility doc](../design/libraries/LibraryImportGenerator/Compatibility.md) for unsupported scenarios). Methods should be marked `LibraryImport` and be `static` and `partial`. diff --git a/docs/coding-guidelines/libraries-packaging.md b/docs/coding-guidelines/libraries-packaging.md index f7c926890bf8e..61829fe545982 100644 --- a/docs/coding-guidelines/libraries-packaging.md +++ b/docs/coding-guidelines/libraries-packaging.md @@ -46,7 +46,7 @@ Most metadata for packages is controlled centrally in the repository and individ Logging abstractions for Microsoft.Extensions.Logging. ``` -Package content can be defined using any of the publicly defined Pack inputs: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets +Package content can be defined using any of the publicly defined Pack inputs: https://learn.microsoft.com/nuget/reference/msbuild-targets ### Package Readme diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 417f6fdec5346..1347b2c0c6b20 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -14,11 +14,11 @@ Read everything in the documented Windows and non-Windows ABI documentation. The ## Windows ABI documentation -AMD64: See [x64 Software Conventions](https://docs.microsoft.com/en-us/cpp/build/x64-software-conventions). +AMD64: See [x64 Software Conventions](https://learn.microsoft.com/cpp/build/x64-software-conventions). -ARM: See [Overview of ARM32 ABI Conventions](https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions). +ARM: See [Overview of ARM32 ABI Conventions](https://learn.microsoft.com/cpp/build/overview-of-arm-abi-conventions). -ARM64: See [Overview of ARM64 ABI conventions](https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions). +ARM64: See [Overview of ARM64 ABI conventions](https://learn.microsoft.com/cpp/build/arm64-windows-abi-conventions). ## Non-Windows ABI documentation diff --git a/docs/design/coreclr/botr/corelib.md b/docs/design/coreclr/botr/corelib.md index c5ff7f387e83f..9662dc3f4e20f 100644 --- a/docs/design/coreclr/botr/corelib.md +++ b/docs/design/coreclr/botr/corelib.md @@ -40,7 +40,7 @@ The CLR provides a [`mscorlib` binder](https://github.com/dotnet/runtime/blob/ma # Calling from managed to native code -Two techniques exist for calling into the CLR from managed code. FCall allows you to call directly into the CLR code, and provides a lot of flexibility in terms of manipulating objects, though it is easy to cause GC holes by not tracking object references correctly. QCall also allows you to call into the CLR via the P/Invoke, but is much harder to accidentally mis-use. FCalls are identified in managed code as extern methods with the [`MethodImplOptions.InternalCall`](https://docs.microsoft.com/dotnet/api/system.runtime.compilerservices.methodimploptions) bit set. QCalls are marked `static extern` methods similar to regular P/Invokes, but are directed toward a library called `"QCall"`. +Two techniques exist for calling into the CLR from managed code. FCall allows you to call directly into the CLR code, and provides a lot of flexibility in terms of manipulating objects, though it is easy to cause GC holes by not tracking object references correctly. QCall also allows you to call into the CLR via the P/Invoke, but is much harder to accidentally mis-use. FCalls are identified in managed code as extern methods with the [`MethodImplOptions.InternalCall`](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.methodimploptions) bit set. QCalls are marked `static extern` methods similar to regular P/Invokes, but are directed toward a library called `"QCall"`. There is a small variant of FCall called HCall (for Helper call) for implementing JIT helpers. The HCall is intended for doing things like accessing multi-dimensional array elements, range checks, etc. The only difference between HCall and FCall is that HCall methods won't show up in an exception stack trace. @@ -50,13 +50,13 @@ First, remember that you should be writing as much as possible in managed code. Reasons to write FCalls in the past generally fell into three camps: missing language features, better performance, or implementing unique interactions with the runtime. C# now has almost every useful language feature that you could get from C++, including unsafe code and stack-allocated buffers, and this eliminates the first two reasons for FCalls. We have ported some parts of the CLR that were heavily reliant on FCalls to managed code in the past (such as Reflection, some Encoding, and String operations) and we intend to continue this momentum. -If the only reason you're defining a FCall method is to call a native method, you should be using P/Invoke to call the method directly. [P/Invoke](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute) is the public native method interface and should be doing everything you need in a correct manner. +If the only reason you're defining a FCall method is to call a native method, you should be using P/Invoke to call the method directly. [P/Invoke](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute) is the public native method interface and should be doing everything you need in a correct manner. If you still need to implement a feature inside the runtime, consider if there is a way to reduce the frequency of transitioning to native code. Can you write the common case in managed and only call into native for some rare corner cases? You're usually best off keeping as much as possible in managed code. QCalls are the preferred mechanism going forward. You should only use FCalls when you are "forced" to. This happens when there is common "short path" through the code that is important to optimize. This short path should not be more than a few hundred instructions, cannot allocate GC memory, take locks or throw exceptions (`GC_NOTRIGGER`, `NOTHROWS`). In all other circumstances (and especially when you enter a FCall and then simply erect HelperMethodFrame), you should be using QCall. -FCalls were specifically designed for short paths of code that must be optimized. They allowed explicit control over when erecting a frame was done. However, it is error prone and not worth the complexity for many APIs. QCalls are essentially P/Invokes into the CLR. In the event the performance of an FCall is required consider creating a QCall and marking it with [`SuppressGCTransitionAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute). +FCalls were specifically designed for short paths of code that must be optimized. They allowed explicit control over when erecting a frame was done. However, it is error prone and not worth the complexity for many APIs. QCalls are essentially P/Invokes into the CLR. In the event the performance of an FCall is required consider creating a QCall and marking it with [`SuppressGCTransitionAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute). As a result, QCalls give you some advantageous marshaling for `SafeHandle`s automatically – your native method just takes a `HANDLE` type, and can be used without worrying whether someone will free the handle while in that method body. The resulting FCall method would need to use a `SafeHandleHolder` and may need to protect the `SafeHandle`, etc. Leveraging the P/Invoke marshaler can avoid this additional plumbing code. diff --git a/docs/design/coreclr/botr/guide-for-porting.md b/docs/design/coreclr/botr/guide-for-porting.md index f7ca105bf165d..25c0aae0e068f 100644 --- a/docs/design/coreclr/botr/guide-for-porting.md +++ b/docs/design/coreclr/botr/guide-for-porting.md @@ -181,7 +181,7 @@ both the JIT and VM. 2. Architecture specific relocation information (to represent generation of relocations for use by load, store, jmp and call instructions) See - + for the sort of details that need to be defined. 3. Behavior and accessibility of processor single step features from within a diff --git a/docs/design/coreclr/jit/viewing-jit-dumps.md b/docs/design/coreclr/jit/viewing-jit-dumps.md index d24611be40211..7d459895f9ae1 100644 --- a/docs/design/coreclr/jit/viewing-jit-dumps.md +++ b/docs/design/coreclr/jit/viewing-jit-dumps.md @@ -115,7 +115,7 @@ For example, for Windows x64 machine, the project file is: ``` - You can find a list of RIDs and their corresponding OSes [here](https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog). + You can find a list of RIDs and their corresponding OSes [here](https://learn.microsoft.com/dotnet/articles/core/rid-catalog). * After you've finished editing the code, run `dotnet restore` and `dotnet publish -c Release`. This should drop all of the binaries needed to run your app in `bin/Release///publish`. * Overwrite the CLR dlls with the ones you've built locally. If you're a fan of the command line, here are some shell commands for doing this: diff --git a/docs/design/coreclr/profiling/davbr-blog-archive/Debugging - Activation.md b/docs/design/coreclr/profiling/davbr-blog-archive/Debugging - Activation.md index 327c90451b9c2..6a3e3b01572fb 100644 --- a/docs/design/coreclr/profiling/davbr-blog-archive/Debugging - Activation.md +++ b/docs/design/coreclr/profiling/davbr-blog-archive/Debugging - Activation.md @@ -1,7 +1,7 @@ *This blog post originally appeared on David Broman's blog on 12/11/2007* -This is the first of some tips to help you debug your profiler. Note that these tips assume you're using CLR 2.x (see [this entry](https://docs.microsoft.com/en-us/archive/blogs/davbr/versions-of-microsoft-net-framework-clr-and-your-profiler) for info on how CLR version numbers map to .NET Framework version numbers). In today's post, I address a frequent question from profiler developers and users: "Why didn't my profiler load?". +This is the first of some tips to help you debug your profiler. Note that these tips assume you're using CLR 2.x (see [this entry](https://learn.microsoft.com/archive/blogs/davbr/versions-of-microsoft-net-framework-clr-and-your-profiler) for info on how CLR version numbers map to .NET Framework version numbers). In today's post, I address a frequent question from profiler developers and users: "Why didn't my profiler load?". ## Event log (Windows only) diff --git a/docs/design/coreclr/profiling/davbr-blog-archive/ELT Hooks - tail calls.md b/docs/design/coreclr/profiling/davbr-blog-archive/ELT Hooks - tail calls.md index d00ceba195e7c..7518db43102d4 100644 --- a/docs/design/coreclr/profiling/davbr-blog-archive/ELT Hooks - tail calls.md +++ b/docs/design/coreclr/profiling/davbr-blog-archive/ELT Hooks - tail calls.md @@ -55,7 +55,7 @@ When you're dealing with languages managed by the CLR, there are two kinds of co ### When does the JIT make tail calls? -I asked Fei Chen and [Grant Richins](https://docs.microsoft.com/en-us/archive/blogs/grantri/), neighbors down the hall from me who happen to work on the JIT, under what conditions the various JITs will employ the tail call optimization. The full answer is rather detailed. The quick summary is that the JITs try to use the tail call optimization whenever they can, but there are lots of reasons why the tail call optimization can't be used. Some reasons why tail calling is a non-option: +I asked Fei Chen and [Grant Richins](https://learn.microsoft.com/archive/blogs/grantri/), neighbors down the hall from me who happen to work on the JIT, under what conditions the various JITs will employ the tail call optimization. The full answer is rather detailed. The quick summary is that the JITs try to use the tail call optimization whenever they can, but there are lots of reasons why the tail call optimization can't be used. Some reasons why tail calling is a non-option: - Caller doesn't return immediately after the call (duh :-)) - Stack arguments between caller and callee are incompatible in a way that would require shifting things around in the caller's frame before the callee could execute diff --git a/docs/design/coreclr/profiling/davbr-blog-archive/Generics and Your Profiler.md b/docs/design/coreclr/profiling/davbr-blog-archive/Generics and Your Profiler.md index 856290fd99311..fce6538b6eb97 100644 --- a/docs/design/coreclr/profiling/davbr-blog-archive/Generics and Your Profiler.md +++ b/docs/design/coreclr/profiling/davbr-blog-archive/Generics and Your Profiler.md @@ -83,7 +83,7 @@ typeArgs: This is the array of type arguments used to instantiate classId, which You may have noticed I ignored this parameter in my description of GetFunctionInfo2. You can pass NULL if you want, and nothing really bad will happen to you, but you’ll often get some incomplete results: you won’t get very useful typeArgs coming back, and you’ll often see NULL returned in \*pClassId. -To understand why, it’s necessary to understand an internal optimization the CLR uses around sharing code for generics: If two instantiations of the same generic function would result in identical JITted code, then why not have them share one copy of that code? The CLR chooses to share code if all of the type parameters are instantiated with reference types. If you want to read more about this, [here’s](https://docs.microsoft.com/en-us/archive/blogs/carlos/net-generics-and-code-bloat-or-its-lack-thereof) a place to go. +To understand why, it’s necessary to understand an internal optimization the CLR uses around sharing code for generics: If two instantiations of the same generic function would result in identical JITted code, then why not have them share one copy of that code? The CLR chooses to share code if all of the type parameters are instantiated with reference types. If you want to read more about this, [here’s](https://learn.microsoft.com/archive/blogs/carlos/net-generics-and-code-bloat-or-its-lack-thereof) a place to go. For now, the important point is that, once we’re inside JITted code that is shared across different generic instantiations, how can one know which instantiation is the actual one that caused the current invocation? Well, in many cases, the CLR may not have that data readily lying around. However, as a profiler, you can capture this information and pass it back to the CLR when it needs it. This is done through a COR\_PRF\_FRAME\_INFO. There are two ways your profiler can get a COR\_PRF\_FRAME\_INFO: @@ -110,7 +110,7 @@ CLR’s generics sharing optimization complicates this somewhat. You’ll reall So that covers JIT notifications—what about ClassLoad\* notifications in the same example? Although the CLR shares _JITted code_ across reference-type instantiations, the CLR still maintains separate loaded _types_ for each generic instantiation of a generic class. So in the example from the paragraph above you will see separate ClassLoad\* notifications with different ClassIDs for MyClass\ and MyClass\. In fact, you will also see a separate ClassLoad\* notification (with yet another ClassID) for MyClass\. -If you got curious, and ran such a profiler under the debugger, you could use the SOS !dumpmt command with those different ClassIDs to see what you get. By doing so, you’ll notice something interesting. !dumpmt shows many values, including “Name”, which will correctly be the specific, fully-instantiated name of the type (different for all three ClassIDs). !dumpmt also shows a thing called “EEClass”. And you’ll notice this “EEClass” value is actually the _same_ for all 3 types. (Remember from this [post](https://docs.microsoft.com/en-us/archive/blogs/davbr/debugging-your-profiler-ii-sos-and-ids) that EEClass is NOT the same thing as ClassID!) That gives you a little window into some additional data sharing optimizations the CLR uses. Stuff that remains the same across different generic instantiations of a class can be stored in a single place (the EEClass) and that single place can be referenced by the different generic instantiations of the class. Note that if you also use a value type as the type argument when instantiating MyClass\ (e.g., MyClass\), and then run !dumpmt on that ClassID, you’ll see an entirely different EEClass value in the output, as the CLR will not be sharing that subset of type data across generic instantiations that use type arguments that are value types. +If you got curious, and ran such a profiler under the debugger, you could use the SOS !dumpmt command with those different ClassIDs to see what you get. By doing so, you’ll notice something interesting. !dumpmt shows many values, including “Name”, which will correctly be the specific, fully-instantiated name of the type (different for all three ClassIDs). !dumpmt also shows a thing called “EEClass”. And you’ll notice this “EEClass” value is actually the _same_ for all 3 types. (Remember from this [post](https://learn.microsoft.com/archive/blogs/davbr/debugging-your-profiler-ii-sos-and-ids) that EEClass is NOT the same thing as ClassID!) That gives you a little window into some additional data sharing optimizations the CLR uses. Stuff that remains the same across different generic instantiations of a class can be stored in a single place (the EEClass) and that single place can be referenced by the different generic instantiations of the class. Note that if you also use a value type as the type argument when instantiating MyClass\ (e.g., MyClass\), and then run !dumpmt on that ClassID, you’ll see an entirely different EEClass value in the output, as the CLR will not be sharing that subset of type data across generic instantiations that use type arguments that are value types. ## Instrumenting Generic Functions diff --git a/docs/design/coreclr/profiling/davbr-blog-archive/Sample A Signature Blob Parser for your Profiler.md b/docs/design/coreclr/profiling/davbr-blog-archive/Sample A Signature Blob Parser for your Profiler.md index 88ab78844b3da..5d9215222f593 100644 --- a/docs/design/coreclr/profiling/davbr-blog-archive/Sample A Signature Blob Parser for your Profiler.md +++ b/docs/design/coreclr/profiling/davbr-blog-archive/Sample A Signature Blob Parser for your Profiler.md @@ -1,7 +1,7 @@ *This blog post originally appeared on David Broman's blog on 10/13/2005* -If your profiler plays with metadata, you've undoubtedly come across signature blobs. They’re used to encode type information for method definitions & references, local variables, and a whole lot more. They’re wonderfully compact, recursively versatile, and sometimes, well, challenging to parse. Fortunately, [Rico Mariani](https://docs.microsoft.com/en-us/archive/blogs/ricom/) was feeling generous one day, and churned out a simple parser that can read these types of signatures: +If your profiler plays with metadata, you've undoubtedly come across signature blobs. They’re used to encode type information for method definitions & references, local variables, and a whole lot more. They’re wonderfully compact, recursively versatile, and sometimes, well, challenging to parse. Fortunately, [Rico Mariani](https://learn.microsoft.com/archive/blogs/ricom/) was feeling generous one day, and churned out a simple parser that can read these types of signatures: - MethodDefSig - MethodRefSig diff --git a/docs/design/datacontracts/Thread.md b/docs/design/datacontracts/Thread.md index 2910dc8dbb820..92382eacf32d5 100644 --- a/docs/design/datacontracts/Thread.md +++ b/docs/design/datacontracts/Thread.md @@ -2,15 +2,16 @@ This contract is for reading and iterating the threads of the process. -## Data structures defined by contract +## APIs of contract + ``` csharp -record struct DacThreadStoreData ( +record struct ThreadStoreData ( int ThreadCount, TargetPointer FirstThread, TargetPointer FinalizerThread, TargetPointer GcThread); -record struct DacThreadStoreCounts ( +record struct ThreadStoreCounts ( int UnstartedThreadCount, int BackgroundThreadCount, int PendingThreadCount, @@ -75,7 +76,7 @@ enum ThreadState TS_Detached = 0x80000000, // Thread was detached by DllMain } -record struct DacThreadData ( +record struct ThreadData ( uint ThreadId; TargetNUint OsThreadId; ThreadState State; @@ -90,11 +91,10 @@ record struct DacThreadData ( ); ``` -## Apis of contract ``` csharp -DacThreadStoreData GetThreadStoreData(); -DacThreadStoreCounts GetThreadCounts(); -DacThreadData GetThreadData(TargetPointer threadPointer); +ThreadStoreData GetThreadStoreData(); +ThreadStoreCounts GetThreadCounts(); +ThreadData GetThreadData(TargetPointer threadPointer); TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException); TargetPointer GetManagedThreadObject(TargetPointer threadPointer); ``` @@ -106,26 +106,26 @@ TargetPointer GetManagedThreadObject(TargetPointer threadPointer); ``` csharp SListReader ThreadListReader = Contracts.SList.GetReader("Thread"); -DacThreadStoreData GetThreadStoreData() +ThreadStoreData GetThreadStoreData() { - TargetPointer threadStore = Target.ReadGlobalTargetPointer("s_pThreadStore"); + TargetPointer threadStore = Target.ReadGlobalPointer("s_pThreadStore"); var runtimeThreadStore = new ThreadStore(Target, threadStore); TargetPointer firstThread = ThreadListReader.GetHead(runtimeThreadStore.SList.Pointer); - return new DacThreadStoreData( + return new ThreadStoreData( ThreadCount : runtimeThreadStore.m_ThreadCount, FirstThread: firstThread, - FinalizerThread: Target.ReadGlobalTargetPointer("g_pFinalizerThread"), - GcThread: Target.ReadGlobalTargetPointer("g_pSuspensionThread")); + FinalizerThread: Target.ReadGlobalPointer("g_pFinalizerThread"), + GCThread: Target.ReadGlobalPointer("g_pSuspensionThread")); } DacThreadStoreCounts GetThreadCounts() { - TargetPointer threadStore = Target.ReadGlobalTargetPointer("s_pThreadStore"); + TargetPointer threadStore = Target.ReadGlobalPointer("s_pThreadStore"); var runtimeThreadStore = new ThreadStore(Target, threadStore); - return new DacThreadStoreCounts( + return new ThreadStoreCounts( ThreadCount : runtimeThreadStore.m_ThreadCount, UnstartedThreadCount : runtimeThreadStore.m_UnstartedThreadCount, BackgroundThreadCount : runtimeThreadStore.m_BackgroundThreadCount, @@ -133,7 +133,7 @@ DacThreadStoreCounts GetThreadCounts() DeadThreadCount: runtimeThreadStore.m_DeadThreadCount, } -DacThreadData GetThreadData(TargetPointer threadPointer) +ThreadData GetThreadData(TargetPointer threadPointer) { var runtimeThread = new Thread(Target, threadPointer); @@ -150,7 +150,7 @@ DacThreadData GetThreadData(TargetPointer threadPointer) firstNestedException = runtimeThread.m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo; } - return new DacThread( + return new ThreadData( ThreadId : runtimeThread.m_ThreadId, OsThreadId : (OsThreadId)runtimeThread.m_OSThreadId, State : (ThreadState)runtimeThread.m_State, @@ -159,7 +159,7 @@ DacThreadData GetThreadData(TargetPointer threadPointer) AllocContextLimit : thread.m_alloc_context.alloc_limit, Frame : thread.m_pFrame, TEB : thread.Has_m_pTEB ? thread.m_pTEB : TargetPointer.Null, - LastThreadObjectHandle : new DacGCHandle(thread.m_LastThrownObjectHandle), + LastThrownObjectHandle : new DacGCHandle(thread.m_LastThrownObjectHandle), FirstNestedException : firstNestedException, NextThread : ThreadListReader.GetHead.GetNext(threadPointer) ); diff --git a/docs/design/datacontracts/contract_csharp_api_design.cs b/docs/design/datacontracts/contract_csharp_api_design.cs index 062c04806003c..a770c1f4576b4 100644 --- a/docs/design/datacontracts/contract_csharp_api_design.cs +++ b/docs/design/datacontracts/contract_csharp_api_design.cs @@ -40,7 +40,7 @@ struct TargetPointer struct TargetNInt { public long Value; - // Add a full set of operators to support arithmetic as well as casting to/from TargetPointer + // Add a full set of operators to support arithmetic as well as casting to/from TargetPointer } struct TargetNUInt @@ -72,84 +72,37 @@ struct FieldLayout public FieldType Type; } - interface IAlgorithmContract - { - void Init(); - } - interface IContract { string Name { get; } uint Version { get; } } - class Target + + sealed class Target { // Users of the data contract may adjust this number to force re-reading of all data public int CurrentEpoch = 0; - sbyte ReadInt8(TargetPointer pointer); - byte ReadUInt8(TargetPointer pointer); - short ReadInt16(TargetPointer pointer); - ushort ReadUInt16(TargetPointer pointer); - int ReadInt32(TargetPointer pointer); - uint ReadUInt32(TargetPointer pointer); - long ReadInt64(TargetPointer pointer); - ulong ReadUInt64(TargetPointer pointer); - TargetPointer ReadTargetPointer(TargetPointer pointer); - TargetNInt ReadNInt(TargetPointer pointer); - TargetNUInt ReadNUint(TargetPointer pointer); + public T Read(ulong address) where T : unmanaged, IBinaryInteger, IMinMaxValue; + TargetPointer ReadPointer(ulong address); + byte[] ReadByteArray(TargetPointer pointer, ulong size); void FillByteArray(TargetPointer pointer, byte[] array, ulong size); - bool TryReadInt8(TargetPointer pointer, out sbyte value); - bool TryReadUInt8(TargetPointer pointer, out byte value); - bool TryReadInt16(TargetPointer pointer, out short value); - bool TryReadUInt16(TargetPointer pointer, out ushort value); - bool TryReadInt32(TargetPointer pointer, out int value); - bool TryReadUInt32(TargetPointer pointer, out uint value); - bool TryReadInt64(TargetPointer pointer, out long value); - bool TryReadUInt64(TargetPointer pointer, out ulong value); - bool TryReadTargetPointer(TargetPointer pointer, out TargetPointer value); - bool TryReadNInt(TargetPointer pointer, out TargetNInt value); - bool TryReadNUInt(TargetPointer pointer, out TargetNUInt value); - bool TryReadByteArray(TargetPointer pointer, ulong size, out byte[] value); - bool TryFillByteArray(TargetPointer pointer, byte[] array, ulong size); - // If pointer is 0, then the return value will be 0 TargetPointer GetTargetPointerForField(TargetPointer pointer, FieldLayout fieldLayout); - sbyte ReadGlobalInt8(string globalName); - byte ReadGlobalUInt8(string globalName); - short ReadGlobalInt16(string globalName); - ushort ReadGlobalUInt16(string globalName); - int ReadGlobalInt32(string globalName); - uint ReadGlobalUInt32(string globalName); - long ReadGlobalInt64(string globalName); - ulong ReadGlobalUInt64(string globalName); - TargetPointer ReadGlobalTargetPointer(string globalName); - - bool TryReadGlobalInt8(string globalName, out sbyte value); - bool TryReadGlobalUInt8(string globalName, out byte value); - bool TryReadGlobalInt16(string globalName, out short value); - bool TryReadGlobalUInt16(string globalName, out ushort value); - bool TryReadGlobalInt32(string globalName, out int value); - bool TryReadGlobalUInt32(string globalName, out uint value); - bool TryReadGlobalInt64(string globalName, out long value); - bool TryReadGlobalUInt64(string globalName, out ulong value); - bool TryReadGlobalTargetPointer(string globalName, out TargetPointer value); - - Contracts Contract { get; } - - partial class Contracts - { - FieldLayout GetFieldLayout(string typeName, string fieldName); - bool TryGetFieldLayout(string typeName, string fieldName, out FieldLayout layout); - int GetTypeSize(string typeName); - bool TryGetTypeSize(string typeName, out int size); + T ReadGlobal(string globalName) where T : unmanaged, IBinaryInteger, IMinMaxValue; + TargetPointer ReadGlobalPointer(string globalName); - object GetContract(string contractName); - bool TryGetContract(string contractName, out object contract); + Contracts.Registry Contracts { get; } + } + // Types defined by contracts live here + namespace Contracts + { + class Registry + { // Every contract that is defined has a field here. As an example this document defines a MethodTableContract // If the contract is not supported by the runtime in use, then the implementation of the contract will be the base type which // is defined to throw if it is ever used. @@ -157,15 +110,6 @@ partial class Contracts // List of contracts will be inserted here by source generator MethodTableContract MethodTableContract; } - } - - // Types defined by contracts live here - namespace ContractDefinitions - { - class CompositeContract - { - List> Subcontracts; - } class DataStructureContract { @@ -173,8 +117,8 @@ class DataStructureContract List> FieldData; } - // Insert Algorithmic Contract definitions here - class MethodTableContract + // Insert contract definitions here + interface MethodTableContract : IContract { public virtual int DynamicTypeID(TargetPointer methodTablePointer) { throw new NotImplementedException(); } public virtual int BaseSize(TargetPointer methodTablePointer) { throw new NotImplementedException(); } @@ -207,7 +151,7 @@ public class FeatureFlags_2 } [DataContractAlgorithm(1)] - class MethodTableContract_1 : ContractDefinitions.MethodTableContract, IAlgorithmContract + readonly struct MethodTableContract_1 : Contracts.MethodTableContract { DataContracts.Target Target; readonly uint ContractVersion; @@ -219,7 +163,7 @@ class MethodTableContract_1 : ContractDefinitions.MethodTableContract, IAlgorith // This is used for version 2 and 3 of the contract, where the dynamic type id is no longer present, and baseSize has a new limitation in that it can only be a value up to 0x1FFFFFFF in v3 [DataContractAlgorithm(2, 3)] - class MethodTableContract_2 : ContractDefinitions.MethodTableContract, IAlgorithmContract + readonly struct MethodTableContract_2 : Contracts.MethodTableContract { DataContracts.Target Target; readonly uint ContractVersion; diff --git a/docs/design/features/COM-activation.md b/docs/design/features/COM-activation.md index 866359ab1802d..d7f6a71cc705f 100644 --- a/docs/design/features/COM-activation.md +++ b/docs/design/features/COM-activation.md @@ -2,7 +2,7 @@ ## Purpose -In order to more fully support the vast number of existing .NET Framework users in their transition to .NET Core, support of the COM activation scenario in .NET Core is required. Without this support it is not possible for many .NET Framework consumers to even consider transitioning to .NET Core. The intent of this document is to describe aspects of COM activation for a .NET class written for .NET Core. This support includes but is not limited to activation scenarios such as the [`CoCreateInstance()`](https://docs.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-cocreateinstance) API in C/C++ or from within a [Windows Script Host](https://docs.microsoft.com/windows/desktop/com/using-com-objects-in-windows-script-host) instance. +In order to more fully support the vast number of existing .NET Framework users in their transition to .NET Core, support of the COM activation scenario in .NET Core is required. Without this support it is not possible for many .NET Framework consumers to even consider transitioning to .NET Core. The intent of this document is to describe aspects of COM activation for a .NET class written for .NET Core. This support includes but is not limited to activation scenarios such as the [`CoCreateInstance()`](https://learn.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-cocreateinstance) API in C/C++ or from within a [Windows Script Host](https://learn.microsoft.com/windows/desktop/com/using-com-objects-in-windows-script-host) instance. COM activation in this document is currently limited to in-proc scenarios. Scenarios involving out-of-proc COM activation are deferred. @@ -10,7 +10,7 @@ COM activation in this document is currently limited to in-proc scenarios. Scena * Discover all installed versions of .NET Core. * Load the appropriate version of .NET Core for the class if a .NET Core instance is not running, or validate the currently existing .NET Core instance can satisfy the class requirement. -* Return an [`IClassFactory`](https://docs.microsoft.com/windows/desktop/api/unknwnbase/nn-unknwnbase-iclassfactory) implementation that will construct an instance of the .NET class. +* Return an [`IClassFactory`](https://learn.microsoft.com/windows/desktop/api/unknwnbase/nn-unknwnbase-iclassfactory) implementation that will construct an instance of the .NET class. * Support the discrimination of concurrently loaded CLR versions. ### Environment matrix @@ -35,7 +35,7 @@ One of the basic issues with the activation of a .NET class within a COM environ The .NET Framework uses a shim library (`mscoree.dll`) to facilitate the loading of the CLR into a process performing activation - one of the many uses of `mscoree.dll`. When .NET Framework 4.0 was released, `mscoreei.dll` was introduced to provide a level of indirection between the system installed shim (`mscoree.dll`) and a specific framework shim as well as to enable side-by-side CLR scenarios. An important consideration of the system wide shim is that of servicing. Servicing `mscoree.dll` is difficult since any process with a loaded .NET Framework instance will have the shim loaded, thus requiring a system reboot in order to service the shim. -During .NET class registration, the shim is identified as the in-proc server for the class. Additional metadata is inserted into the registry to indicate what .NET assembly to load and what type to activate. For example, in addition to the typical [in-proc server](https://docs.microsoft.com/windows/desktop/com/inprocserver32) registry values the following values are added to the registry for the `TypeLoadException` class. +During .NET class registration, the shim is identified as the in-proc server for the class. Additional metadata is inserted into the registry to indicate what .NET assembly to load and what type to activate. For example, in addition to the typical [in-proc server](https://learn.microsoft.com/windows/desktop/com/inprocserver32) registry values the following values are added to the registry for the `TypeLoadException` class. ``` "Assembly"="mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" @@ -43,7 +43,7 @@ During .NET class registration, the shim is identified as the in-proc server for "RuntimeVersion"="v1.1.4322" ``` -The above registration is typically done with the [`RegAsm.exe`](https://docs.microsoft.com/dotnet/framework/tools/regasm-exe-assembly-registration-tool) tool. Alternatively, registry scripts can be generated by `RegAsm.exe`. +The above registration is typically done with the [`RegAsm.exe`](https://learn.microsoft.com/dotnet/framework/tools/regasm-exe-assembly-registration-tool) tool. Alternatively, registry scripts can be generated by `RegAsm.exe`. ### .NET Core class COM activation @@ -51,9 +51,9 @@ In .NET Core, our intent will be to avoid a system wide shim library. This decis The current .NET Core hosting solutions are described in detail at [Documentation/design-docs/host-components.md](https://github.com/dotnet/runtime/tree/main/docs/design/features/host-components.md). Along with the existing hosts an additional customizable COM activation host library (`comhost.dll`) will be added. This library (henceforth identified as 'shim') will export the required functions for COM class activation and registration and act in a way similar to .NET Framework's `mscoree.dll`. ->[`HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv);`](https://docs.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject) +>[`HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv);`](https://learn.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-dllgetclassobject) ->[`HRESULT DllCanUnloadNow();`](https://docs.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-dllcanunloadnow) +>[`HRESULT DllCanUnloadNow();`](https://learn.microsoft.com/windows/desktop/api/combaseapi/nf-combaseapi-dllcanunloadnow) >[`HRESULT DllRegisterServer();`](https://msdn.microsoft.com/library/windows/desktop/ms682162(v=vs.85).aspx) @@ -63,7 +63,7 @@ When `DllGetClassObject()` is called in a COM activation scenario, the following 1) Determine additional registration information needed for activation. * The shim will check for an embedded manifest. If the shim does not contain an embedded manifest, the shim will check if a file with the `.clsidmap` naming format exists adjacent to it. Build tooling handles shim customization, including renaming the shim to be based on the managed assembly's name (e.g. `NetComServer.dll` will have a custom shim called `NetComServer.comhost.dll`). If the shim is signed the shim will **not** attempt to discover the manifest on disk. - * The manifest will contain a mapping from [`CLSID`](https://docs.microsoft.com/windows/desktop/com/com-class-objects-and-clsids) to managed assembly name and the [Fully-Qualified Name](https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) for the type. The format of this manifest is defined below. The shim's embedded mapping always takes precedence and in the case an embedded mapping is found, a `.clsidmap` file on disk will never be used. + * The manifest will contain a mapping from [`CLSID`](https://learn.microsoft.com/windows/desktop/com/com-class-objects-and-clsids) to managed assembly name and the [Fully-Qualified Name](https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) for the type. The format of this manifest is defined below. The shim's embedded mapping always takes precedence and in the case an embedded mapping is found, a `.clsidmap` file on disk will never be used. * The manifest will define an exhaustive list of .NET classes the shim is permitted to provide. * If a [`.runtimeconfig.json`](https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md) file exists adjacent to the target managed assembly (`.runtimeconfig.json`), that file is used to describe the target framework and CLR configuration. The documentation for the `.runtimeconfig.json` format defines under what circumstances this file may be optional. 1) The `DllGetClassObject()` function verifies the `CLSID` mapping has a mapping for the `CLSID`. @@ -71,7 +71,7 @@ When `DllGetClassObject()` is called in a COM activation scenario, the following 1) The shim attempts to load the latest version of the `hostfxr` library and retrieves the `hostfxr_initialize_for_runtime_config()` and `hostfxr_get_runtime_delegate()` exports. 1) The target assembly name is computed by stripping off the `.comhost.dll` prefix and replacing it with `.dll`. Using the name of the target assembly, the path to the `.runtimeconfig.json` file is then computed. 1) The `hostfxr_initialize_for_runtime_config()` export is called. -1) Based on the `.runtimeconfig.json` the [framework](https://docs.microsoft.com/dotnet/core/packages#frameworks) to use can be determined and the appropriate `hostpolicy` library path is computed. +1) Based on the `.runtimeconfig.json` the [framework](https://learn.microsoft.com/dotnet/core/packages#frameworks) to use can be determined and the appropriate `hostpolicy` library path is computed. 1) The `hostpolicy` library is loaded and various exports are retrieved. * If a `hostpolicy` instance is already loaded, the one presently loaded is re-used. * If a CLR is active within the process, the requested CLR version will be validated against that CLR. If version satisfiability fails, activation will fail. @@ -104,17 +104,17 @@ When `DllGetClassObject()` is called in a COM activation scenario, the following } ``` Note this API is not exposed outside of `System.Private.CoreLib` and is subject to change at any time. - * The loading of the assembly will take place in a new [`AssemblyLoadContext`](https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) for dependency isolation. Each assembly path will get a separate `AssemblyLoadContext`. This means that if an assembly provides multiple COM servers all of the servers from that assembly will reside in the same `AssemblyLoadContext`. + * The loading of the assembly will take place in a new [`AssemblyLoadContext`](https://learn.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) for dependency isolation. Each assembly path will get a separate `AssemblyLoadContext`. This means that if an assembly provides multiple COM servers all of the servers from that assembly will reside in the same `AssemblyLoadContext`. * The created `AssemblyLoadContext` will use an [`AssemblyDependencyResolver`](https://github.com/dotnet/runtime/issues/27787) that was supplied with the path to the assembly to load assemblies. 1) The `IClassFactory` instance is returned to the caller of `DllGetClassObject()` to attempt class activation. The `DllCanUnloadNow()` function will always return `S_FALSE` indicating the shim is never able to be unloaded. This matches .NET Framework semantics but may be adjusted in the future if needed. -The `DllRegisterServer()` and `DllUnregisterServer()` functions adhere to the [COM registration contract](https://docs.microsoft.com/windows/desktop/com/classes-and-servers) and enable registration and unregistration of the classes defined in the `CLSID` mapping manifest. Discovery of the mapping manifest is identical to that which occurs during a call to `DllGetClassObject()`. +The `DllRegisterServer()` and `DllUnregisterServer()` functions adhere to the [COM registration contract](https://learn.microsoft.com/windows/desktop/com/classes-and-servers) and enable registration and unregistration of the classes defined in the `CLSID` mapping manifest. Discovery of the mapping manifest is identical to that which occurs during a call to `DllGetClassObject()`. ##### CLSID map format -The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on disk) that defines a mapping from `CLSID` to an assembly name and type name tuple as well as an optional [ProgID](https://docs.microsoft.com/windows/win32/com/-progid--key). Each `CLSID` mapping is a key in the outer JSON object. +The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on disk) that defines a mapping from `CLSID` to an assembly name and type name tuple as well as an optional [ProgID](https://learn.microsoft.com/windows/win32/com/-progid--key). Each `CLSID` mapping is a key in the outer JSON object. ``` json { @@ -129,9 +129,9 @@ The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on dis ### .NET Core COM server creation 1) A new .NET Core class library project is created using [`dotnet.exe`][dotnet_link]. -1) A class is defined that has the [`GuidAttribute("")`][guid_link] and the [`ComVisibleAttribute(true)`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.comvisibleattribute). +1) A class is defined that has the [`GuidAttribute("")`][guid_link] and the [`ComVisibleAttribute(true)`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.comvisibleattribute). - In .NET Core, unlike .NET Framework, there is no generated class interface generation (i.e. `IClassX`). This means it is advantageous for users to have the class implement a marshalable interface. - - A ProgID for the class can be defined using the [`ProgIdAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.progidattribute). If a ProgID is not explicitly specified, the namespace and class name will be used as the ProgID. This follows the same semantics as .NET Framework COM servers. + - A ProgID for the class can be defined using the [`ProgIdAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.progidattribute). If a ProgID is not explicitly specified, the namespace and class name will be used as the ProgID. This follows the same semantics as .NET Framework COM servers. 1) The `EnableComHosting` property is added to the project file. - i.e. `true` 1) During class project build, the following actions occur if the `EnableComHosting` property is `true`: @@ -147,11 +147,11 @@ Two options exist for registration and are a function of the intent of the class #### Registry -Class registration in the registry for .NET Core classes is greatly simplified and is now identical to that of a non-managed COM class. This is possible due to the presence of the aforementioned `.clsidmap` manifest. The application developer will be able to use the traditional [`regsvr32.exe`](https://docs.microsoft.com/windows-server/administration/windows-commands/regsvr32) tool for class registration. +Class registration in the registry for .NET Core classes is greatly simplified and is now identical to that of a non-managed COM class. This is possible due to the presence of the aforementioned `.clsidmap` manifest. The application developer will be able to use the traditional [`regsvr32.exe`](https://learn.microsoft.com/windows-server/administration/windows-commands/regsvr32) tool for class registration. #### Registration-Free -[RegFree COM for .NET](https://docs.microsoft.com/dotnet/framework/interop/configure-net-framework-based-com-components-for-reg) is another style of registration, but does not require registry access. This approach is complicated by the use of [application manifests](https://docs.microsoft.com/windows/desktop/SbsCs/application-manifests), but does have benefits for limiting environment impact and simplifying deployment. A severe limitation of this approach is that in order to use RegFree COM with a .NET class, the Window OS assumes the use of `mscoree.dll` for the in-proc server. Without a change in the Windows OS, this assumption in the RegFree .NET scenario makes the existing manifest approach a broken scenario for .NET Core. +[RegFree COM for .NET](https://learn.microsoft.com/dotnet/framework/interop/configure-net-framework-based-com-components-for-reg) is another style of registration, but does not require registry access. This approach is complicated by the use of [application manifests](https://learn.microsoft.com/windows/desktop/SbsCs/application-manifests), but does have benefits for limiting environment impact and simplifying deployment. A severe limitation of this approach is that in order to use RegFree COM with a .NET class, the Window OS assumes the use of `mscoree.dll` for the in-proc server. Without a change in the Windows OS, this assumption in the RegFree .NET scenario makes the existing manifest approach a broken scenario for .NET Core. An example of a RegFree manifest for a .NET Framework class is below - note the absence of specifying a hosting server library (i.e. `mscoree.dll` is implied for the `clrClass` element). @@ -196,7 +196,7 @@ The .NET Core steps for RegFree are as follows: ``` -1) The tool chain can optionally generate a [SxS](https://docs.microsoft.com/windows/desktop/sbscs/about-side-by-side-assemblies-) manifest for the shim. Both the SxS manifest _and_ the shim library will need to be app-local for the scenario to work. Note that the application developer is responsible for adding to or merging the generated shim's manifest with one the user may have defined for other scenarios. An example shim manifest is defined below and with it the SxS logic will naturally know to query the shim for the desired class. Note that multiple `comClass` tags can be added. +1) The tool chain can optionally generate a [SxS](https://learn.microsoft.com/windows/desktop/sbscs/about-side-by-side-assemblies-) manifest for the shim. Both the SxS manifest _and_ the shim library will need to be app-local for the scenario to work. Note that the application developer is responsible for adding to or merging the generated shim's manifest with one the user may have defined for other scenarios. An example shim manifest is defined below and with it the SxS logic will naturally know to query the shim for the desired class. Note that multiple `comClass` tags can be added. ``` xml @@ -231,10 +231,10 @@ The .NET Core steps for RegFree are as follows: [Calling a .NET Component from a COM Component](https://msdn.microsoft.com/library/ms973802.aspx) -[Using COM Types in Managed Code](https://docs.microsoft.com/previous-versions/dotnet/netframework-4.0/3y76b69k%28v%3dvs.100%29) +[Using COM Types in Managed Code](https://learn.microsoft.com/previous-versions/dotnet/netframework-4.0/3y76b69k%28v%3dvs.100%29) -[Exposing .NET Framework Components to COM](https://docs.microsoft.com/dotnet/framework/interop/exposing-dotnet-components-to-com) +[Exposing .NET Framework Components to COM](https://learn.microsoft.com/dotnet/framework/interop/exposing-dotnet-components-to-com) -[guid_link]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.guidattribute -[dotnet_link]: https://docs.microsoft.com/dotnet/core/tools/dotnet +[guid_link]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.guidattribute +[dotnet_link]: https://learn.microsoft.com/dotnet/core/tools/dotnet diff --git a/docs/design/features/IJW-activation.md b/docs/design/features/IJW-activation.md index d55032e46706a..3ae8e64c72ebe 100644 --- a/docs/design/features/IJW-activation.md +++ b/docs/design/features/IJW-activation.md @@ -44,7 +44,7 @@ Below are the entry-points that the Visual C++ team needs When `_CorExeMain()` is called, the following will occur: 1) If a [`.runtimeconfig.json`](https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md) file exists adjacent to the shim assembly (`.runtimeconfig.json`), that file will be used to describe CLR configuration details. The documentation for the `.runtimeconfig.json` format defines under what circumstances this file may be optional. -2) Using the existing `hostfxr` library, attempt to discover the desired CLR and target [framework](https://docs.microsoft.com/en-us/dotnet/core/packages#frameworks). +2) Using the existing `hostfxr` library, attempt to discover the desired CLR and target [framework](https://learn.microsoft.com/dotnet/core/packages#frameworks). * If a CLR is active with the process, the requested CLR version will be validated against that CLR. If version satisfiability fails, activation will fail. * If a CLR is **not** active with the process, an attempt will be made to create a satisfying CLR instance. * Failure to create an instance will result in activation failure. diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md index dec8c64a42db2..d529f18f140df 100644 --- a/docs/design/features/byreflike-generics.md +++ b/docs/design/features/byreflike-generics.md @@ -84,13 +84,13 @@ namespace System.Runtime.CompilerServices Current examples of APIs that would need the attribute applied: -- [`Span`](https://docs.microsoft.com/dotnet/api/system.span-1) +- [`Span`](https://learn.microsoft.com/dotnet/api/system.span-1) - `public Span(T[]? array);` - `public Span(T[]? array, int start, int length);` - `public T[] ToArray();` - `public static implicit operator Span(ArraySegment segment);` - `public static implicit operator Span(T[]? array);` -- [`ReadOnlySpan`](https://docs.microsoft.com/dotnet/api/system.readonlyspan-1) +- [`ReadOnlySpan`](https://learn.microsoft.com/dotnet/api/system.readonlyspan-1) - `public ReadOnlySpan(T[]? array);` - `public ReadOnlySpan(T[]? array, int start, int length);` - `public T[] ToArray();` diff --git a/docs/design/features/dllmap.md b/docs/design/features/dllmap.md index b1c710e5bf7ee..e0e113e762e99 100644 --- a/docs/design/features/dllmap.md +++ b/docs/design/features/dllmap.md @@ -21,9 +21,9 @@ Mono also permits mapping of method names within libraries, but with the restric .Net Core 3 provides a rich set of APIs to manage native libraries, as well as callbacks to influence native library resolution. -- [NativeLibrary APIs](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativelibrary?view=netcore-3.0): Perform operations on native libraries (such as `Load()`, `Free()`, get the address of an exported symbol, etc.) in a platform-independent way from managed code. -- [DllImport Resolver callback](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativelibrary.setdllimportresolver?view=netcore-3.0): Gets a callback for first-chance native library resolution using custom logic. -- [Native Library Resolve event](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext.resolvingunmanageddll?view=netcore-3.0): Get an event for last-chance native library resolution using custom logic. +- [NativeLibrary APIs](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.nativelibrary?view=netcore-3.0): Perform operations on native libraries (such as `Load()`, `Free()`, get the address of an exported symbol, etc.) in a platform-independent way from managed code. +- [DllImport Resolver callback](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.nativelibrary.setdllimportresolver?view=netcore-3.0): Gets a callback for first-chance native library resolution using custom logic. +- [Native Library Resolve event](https://learn.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext.resolvingunmanageddll?view=netcore-3.0): Get an event for last-chance native library resolution using custom logic. These APIs can be used to implement custom native library resolution logic, including Mono-style DllMap. diff --git a/docs/design/features/globalization-hybrid-mode.md b/docs/design/features/globalization-hybrid-mode.md index 001ae20002cc9..2270fe897c1c0 100644 --- a/docs/design/features/globalization-hybrid-mode.md +++ b/docs/design/features/globalization-hybrid-mode.md @@ -270,8 +270,8 @@ Dependencies: Web API does not expose locale-sensitive endsWith/startsWith function. As a workaround, both strings get normalized and weightless characters are removed. Resulting strings are cut to the same length and comparison is performed. This approach, beyond having the same compare option limitations as described under **String comparison**, has additional limitations connected with the workaround used. Because we are normalizing strings to be able to cut them, we cannot calculate the match length on the original strings. Methods that calculate this information throw PlatformNotSupported exception: -- [CompareInfo.IsPrefix](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.compareinfo.isprefix?view=net-8.0#system-globalization-compareinfo-isprefix(system-readonlyspan((system-char))-system-readonlyspan((system-char))-system-globalization-compareoptions-system-int32@)) -- [CompareInfo.IsSuffix](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.compareinfo.issuffix?view=net-8.0#system-globalization-compareinfo-issuffix(system-readonlyspan((system-char))-system-readonlyspan((system-char))-system-globalization-compareoptions-system-int32@)) +- [CompareInfo.IsPrefix](https://learn.microsoft.com/dotnet/api/system.globalization.compareinfo.isprefix?view=net-8.0#system-globalization-compareinfo-isprefix(system-readonlyspan((system-char))-system-readonlyspan((system-char))-system-globalization-compareoptions-system-int32@)) +- [CompareInfo.IsSuffix](https://learn.microsoft.com/dotnet/api/system.globalization.compareinfo.issuffix?view=net-8.0#system-globalization-compareinfo-issuffix(system-readonlyspan((system-char))-system-readonlyspan((system-char))-system-globalization-compareoptions-system-int32@)) - `IgnoreSymbols` Only comparisons that do not skip character types are allowed. E.g. `IgnoreSymbols` skips symbol-chars in comparison/indexing. All `CompareOptions` combinations that include `IgnoreSymbols` throw `PlatformNotSupportedException`. diff --git a/docs/design/features/globalization-invariant-mode.md b/docs/design/features/globalization-invariant-mode.md index 62544139522dd..0b71ffc992d38 100644 --- a/docs/design/features/globalization-invariant-mode.md +++ b/docs/design/features/globalization-invariant-mode.md @@ -2,7 +2,7 @@ Author: [Tarek Mahmoud Sayed](https://github.com/tarekgh) -The globalization invariant mode - new in .NET Core 2.0 - enables you to remove application dependencies on globalization data and [globalization behavior](https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/). This mode is an opt-in feature that provides more flexibility if you care more about reducing dependencies and the size of distribution than globalization functionality or globalization-correctness. +The globalization invariant mode - new in .NET Core 2.0 - enables you to remove application dependencies on globalization data and [globalization behavior](https://learn.microsoft.com/dotnet/standard/globalization-localization/). This mode is an opt-in feature that provides more flexibility if you care more about reducing dependencies and the size of distribution than globalization functionality or globalization-correctness. The drawback of running in the invariant mode is applications will get poor globalization support. This new option is only recommended for developers that understand globalization and the impact of its absence. @@ -54,9 +54,9 @@ Turkish I casing will not be supported when using Turkish cultures. ## String sorting and searching -String operations like [Compare](https://docs.microsoft.com/dotnet/api/?term=string.compare), [IndexOf](https://docs.microsoft.com/dotnet/api/?term=string.indexof) and [LastIndexOf](https://docs.microsoft.com/dotnet/api/?term=string.lastindexof) are always performed as [ordinal](https://en.wikipedia.org/wiki/Ordinal_number) and not linguistic operations regardless of the string comparing options passed to the APIs. +String operations like [Compare](https://learn.microsoft.com/dotnet/api/?term=string.compare), [IndexOf](https://learn.microsoft.com/dotnet/api/?term=string.indexof) and [LastIndexOf](https://learn.microsoft.com/dotnet/api/?term=string.lastindexof) are always performed as [ordinal](https://en.wikipedia.org/wiki/Ordinal_number) and not linguistic operations regardless of the string comparing options passed to the APIs. -The [ignore case](https://docs.microsoft.com/dotnet/api/system.globalization.compareoptions.ignorecase) string sorting option is supported but only for the ASCII range as mentioned previously. +The [ignore case](https://learn.microsoft.com/dotnet/api/system.globalization.compareoptions.ignorecase) string sorting option is supported but only for the ASCII range as mentioned previously. For example, the following comparison will resolve to being unequal: @@ -71,7 +71,7 @@ However, the following comparison will resolve to being equal: * 'I', using * CompareOptions.Ignorecase -It is worth noticing that all other [sort comparison options](https://docs.microsoft.com/dotnet/api/system.globalization.compareoptions) (for example, ignore symbols, ignore space, Katakana, Hiragana) will have no effect in the invariant mode (they are ignored). +It is worth noticing that all other [sort comparison options](https://learn.microsoft.com/dotnet/api/system.globalization.compareoptions) (for example, ignore symbols, ignore space, Katakana, Hiragana) will have no effect in the invariant mode (they are ignored). ## Sort keys diff --git a/docs/design/features/hosting-layer-apis.md b/docs/design/features/hosting-layer-apis.md index 37d26972ae79a..0eab1666429bb 100644 --- a/docs/design/features/hosting-layer-apis.md +++ b/docs/design/features/hosting-layer-apis.md @@ -4,7 +4,7 @@ Functionality for advanced hosting scenarios is exposed on the `hostfxr` and `ho The `char_t` strings in the below API descriptions are defined based on the platform: * Windows - UTF-16 (2-byte `wchar_t`) - * Note that `wchar_t` is defined as a [native type](https://docs.microsoft.com/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type), which is the default in Visual Studio. + * Note that `wchar_t` is defined as a [native type](https://learn.microsoft.com/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type), which is the default in Visual Studio. * Unix - UTF-8 (1-byte `char`) ## Host FXR diff --git a/docs/design/features/localization-options.md b/docs/design/features/localization-options.md index 6b76f525a4782..f23fbdcfb9062 100644 --- a/docs/design/features/localization-options.md +++ b/docs/design/features/localization-options.md @@ -15,9 +15,9 @@ The goal is to support: ### Windows -On Windows, [resource script (.rc) files](https://docs.microsoft.com/cpp/windows/resource-files-visual-studio) are used to create resources that will be embedded into a binary. These files define [`STRINGTABLE`](https://docs.microsoft.com/windows/win32/menurc/stringtable-resource) resources containing the resource strings. Each string has a resource identifier - a symbol name mapped to an integer value - which can be used to look up the string value. +On Windows, [resource script (.rc) files](https://learn.microsoft.com/cpp/windows/resource-files-visual-studio) are used to create resources that will be embedded into a binary. These files define [`STRINGTABLE`](https://learn.microsoft.com/windows/win32/menurc/stringtable-resource) resources containing the resource strings. Each string has a resource identifier - a symbol name mapped to an integer value - which can be used to look up the string value. -The [`LoadString`](https://docs.microsoft.com/windows/win32/api/winuser/nf-winuser-loadstringw) and [`FormatMessage`](https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-formatmessage) APIs retrieve a string resources based on a specified identifier (the integer value of the resource identifier) from a specified module. These APIs leave it to their consumer to find and load the appropriate module containing the desired resources. While resources for all languages can be included in the main binary itself, it is common to separate language-specific resources into resource-only libraries. +The [`LoadString`](https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-loadstringw) and [`FormatMessage`](https://learn.microsoft.com/windows/win32/api/winbase/nf-winbase-formatmessage) APIs retrieve a string resources based on a specified identifier (the integer value of the resource identifier) from a specified module. These APIs leave it to their consumer to find and load the appropriate module containing the desired resources. While resources for all languages can be included in the main binary itself, it is common to separate language-specific resources into resource-only libraries. ### Linux @@ -284,7 +284,7 @@ Any automated testing would likely also require some form of language override. ### Single-file -The standard way of doing native localization is based on having separate resources files. On Windows, it is possible to embed resources for multiple languages into one library and use [`FormatMessage`](https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-formatmessage) or a combination of [`FindResourceEx`](https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-findresourceexa) and [`LoadResource`](https://docs.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-loadresource) to load the resource for a specific language. On Linux and OSX, no such platform support exists. +The standard way of doing native localization is based on having separate resources files. On Windows, it is possible to embed resources for multiple languages into one library and use [`FormatMessage`](https://learn.microsoft.com/windows/win32/api/winbase/nf-winbase-formatmessage) or a combination of [`FindResourceEx`](https://learn.microsoft.com/windows/win32/api/winbase/nf-winbase-findresourceexa) and [`LoadResource`](https://learn.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-loadresource) to load the resource for a specific language. On Linux and OSX, no such platform support exists. Extracting files to disk has proven to be extremely problematic across all platforms (permissions, anti-virus, clean up). Adding native resources to that extraction would only exacerbate the existing issues. This means that localized resources would need be read from memory. A custom solution would need to be created and maintained: - Format for storing identifiers and their corresponding strings for every language diff --git a/docs/design/features/sharedfx-lookup.md b/docs/design/features/sharedfx-lookup.md index 870bee16cdd43..7f5532e45c9c7 100644 --- a/docs/design/features/sharedfx-lookup.md +++ b/docs/design/features/sharedfx-lookup.md @@ -4,7 +4,7 @@ There are two main ways of running .NET Applications: through `dotnet` or through the `apphost` executables. The executable is in charge of finding and loading `hostfxr`. `hostfxr`, in turn, must find and load `hostpolicy`. It is also responsible for searching for the SDK when running .NET SDK commands. Finally, `hostpolicy` must find and load the runtime (`coreclr`). See [host components](host-components.md) for details. -An application can either be [framework-dependent](https://docs.microsoft.com/dotnet/core/deploying/#publish-framework-dependent) or [self-contained](https://docs.microsoft.com/dotnet/core/deploying/#publish-self-contained). Framework-dependent apps must have the runtime files inside predefined folders. Self-contained apps are expected to have their dependencies in the same location as the executable. +An application can either be [framework-dependent](https://learn.microsoft.com/dotnet/core/deploying/#publish-framework-dependent) or [self-contained](https://learn.microsoft.com/dotnet/core/deploying/#publish-self-contained). Framework-dependent apps must have the runtime files inside predefined folders. Self-contained apps are expected to have their dependencies in the same location as the executable. ## Semantic Versioning @@ -42,7 +42,7 @@ There are two possibilities for a muxer: it can be a framework-dependent app or In the first case the app file path should have been specified as an argument to the dotnet.exe. In the second case the `dotnet.dll` from SDK must be invoked as a framework-dependent app. At first the running program searches for the `global.json` file which may have specified a CLI version. It starts from the current working directory and looks for it inside all parent folder hierarchy. After that, it searches for the dotnet.dll file inside the `sdk\` sub-folder in the executable directory. -The exact algorithm how versions as matched is described (with some history) in the [docs](https://docs.microsoft.com/en-us/dotnet/core/tools/global-json#matching-rules) +The exact algorithm how versions as matched is described (with some history) in the [docs](https://learn.microsoft.com/dotnet/core/tools/global-json#matching-rules) Note: if the SDK lookup is invoked through `hostfxr_resolve_sdk2` the algorithm is the same, expect that the function can disallow pre-release versions via the `hostfxr_resolve_sdk2_flags_t::disallow_prerelease` flag. diff --git a/docs/design/features/source-generator-pinvokes.md b/docs/design/features/source-generator-pinvokes.md index d13581b9353ff..f9d1a3b9039f4 100644 --- a/docs/design/features/source-generator-pinvokes.md +++ b/docs/design/features/source-generator-pinvokes.md @@ -2,7 +2,7 @@ ## Purpose -The CLR possesses a rich built-in marshaling mechanism for interoperability with native code that is handled at runtime. This system was designed to free .NET developers from having to author complex and potentially ABI sensitive [type conversion code][typemarshal_link] from a managed to an unmanaged environment. The built-in system works with both [P/Invoke][pinvoke_link] (i.e. `DllImportAttribute`) and [COM interop](https://docs.microsoft.com/dotnet/standard/native-interop/cominterop). The generated portion is typically called an ["IL Stub"][il_stub_link] since the stub is generated by inserting IL instructions into a stream and then passing that stream to the JIT for compilation. +The CLR possesses a rich built-in marshaling mechanism for interoperability with native code that is handled at runtime. This system was designed to free .NET developers from having to author complex and potentially ABI sensitive [type conversion code][typemarshal_link] from a managed to an unmanaged environment. The built-in system works with both [P/Invoke][pinvoke_link] (i.e. `DllImportAttribute`) and [COM interop](https://learn.microsoft.com/dotnet/standard/native-interop/cominterop). The generated portion is typically called an ["IL Stub"][il_stub_link] since the stub is generated by inserting IL instructions into a stream and then passing that stream to the JIT for compilation. A consequence of this approach is that marshaling code is not immediately available post-link for AOT scenarios (e.g. [`crossgen`](../../workflow/building/coreclr/crossgen.md) and [`crossgen2`](crossgen2-compilation-structure-enhancements.md)). The immediate unavailability of this code has been mitigated by a complex mechanism to have marshalling code generated by during AOT compilation. The [IL Linker][ilinker_link] is another tool that struggles with runtime generated code since it is unable to understand all potential used types without seeing what is generated. @@ -10,7 +10,7 @@ The user experience of the built-in generation initially appears ideal, but ther * Bug fixes in the marshaling system require an update to the entire runtime. * New types require enhancements to the marshaling system for efficient marshal behavior. - * [`ICustomMarshaler`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler) incurs a substantial performance penalty. + * [`ICustomMarshaler`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler) incurs a substantial performance penalty. * Once a marshaling bug becomes expected behavior the bug is difficult to fix. This is due to user reliance on shipped behavior and since the marshaling system is built into the runtime there aren't ways to select previous or new behavior. * Example involving COM marshaling: https://github.com/dotnet/coreclr/pull/23974. * Debugging the auto-generated marshaling IL Stub is difficult for runtime developers and close to impossible for consumers of P/Invokes. @@ -26,7 +26,7 @@ The [Roslyn Compiler](https://github.com/dotnet/roslyn) team is working on a [So * [Source Generators][source_gen_link] * Branch: https://github.com/dotnet/roslyn/tree/features/source-generators -* Support for non-`void` return types in [`partial`](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/partial-method) methods. +* Support for non-`void` return types in [`partial`](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/partial-method) methods. * https://github.com/dotnet/csharplang/issues/3301 ## Design @@ -61,11 +61,11 @@ At (A) in the above code snippet, the runtime is told to look for an export name 2) The runtime attempts to find a binary with the name supplied in `DllImportAttribute`. - * Discovery of the target binary is complicated and can be influenced by the [`AssemblyLoadContext`](https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) and [`NativeLibrary`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.nativelibrary) classes. + * Discovery of the target binary is complicated and can be influenced by the [`AssemblyLoadContext`](https://learn.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext) and [`NativeLibrary`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.nativelibrary) classes. -3) Once the binary is found and loaded into the runtime, it is queried for the expected export name. The name of the attributed function is used by default but this is configurable by the [`DllImportAttribute.EntryPoint`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.entrypoint) property. +3) Once the binary is found and loaded into the runtime, it is queried for the expected export name. The name of the attributed function is used by default but this is configurable by the [`DllImportAttribute.EntryPoint`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.entrypoint) property. - * This process is also influenced by additional `DllImportAttribute` properties as well as by the underlying platform. For example, the [Win32 API ANSI/UNICODE convention](https://docs.microsoft.com/windows/win32/intl/conventions-for-function-prototypes) on Windows is respected and a(n) `A`/`W` suffix may be appended to the function name if it is not immediately found. + * This process is also influenced by additional `DllImportAttribute` properties as well as by the underlying platform. For example, the [Win32 API ANSI/UNICODE convention](https://learn.microsoft.com/windows/win32/intl/conventions-for-function-prototypes) on Windows is respected and a(n) `A`/`W` suffix may be appended to the function name if it is not immediately found. 4) The IL Stub is called like any other .NET method. @@ -111,7 +111,7 @@ During the source generation process the metadata in the `LibraryImportAttribute The Source Generator would generate the implementation of the partial method (D) in a separate translation unit (`Stubs.g.cs`). At point (E) a `DllImportAttribute` declaration is created based on the user's original declaration (A) for a private P/Invoke specifically for the generated code. The P/Invoke signature from the original declaration would be modified to contain only [blittable types][blittable_link] to ensure the JIT could inline the invocation. Finally note that the user's original function signature would remain in to avoid impacting existing callsites. -In this system it is not defined how marshaling of specific types would be performed. The built-in runtime has complex rules for some types, and it is these rules that once shipped become the de facto standard - often times regardless if the behavior is a bug or not. The design here is not concerned with how the arguments go from a managed to unmanaged environment. With the IL Stub generation extracted from the runtime new type marshaling (e.g. [`Span`](https://docs.microsoft.com/dotnet/api/system.span-1)) could be introduced without requiring an corresponding update to the runtime itself. The `Span` type is good example of a type that at present has no support for marshaling, but with this proposal users could update to the latest generator and have support without changing the runtime. +In this system it is not defined how marshaling of specific types would be performed. The built-in runtime has complex rules for some types, and it is these rules that once shipped become the de facto standard - often times regardless if the behavior is a bug or not. The design here is not concerned with how the arguments go from a managed to unmanaged environment. With the IL Stub generation extracted from the runtime new type marshaling (e.g. [`Span`](https://learn.microsoft.com/dotnet/api/system.span-1)) could be introduced without requiring an corresponding update to the runtime itself. The `Span` type is good example of a type that at present has no support for marshaling, but with this proposal users could update to the latest generator and have support without changing the runtime. ### Adoption of Source Generator @@ -199,11 +199,11 @@ namespace System.Runtime.InteropServices * How will users get error messages during source generator? - * The Source Generator API will be permitted to provide warnings and errors through the [Roslyn SDK](https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/). + * The Source Generator API will be permitted to provide warnings and errors through the [Roslyn SDK](https://learn.microsoft.com/dotnet/csharp/roslyn-sdk/). * Will it be possible to completely replicate the marshaling rules in the current built-in system using existing .NET APIs? - * No. There are rules and semantics that would be difficult to replicate with the current .NET API surface. Additional .NET APIs will likely need to be added in order to allow a Source Generator implementation to provide identical semantics with the built-in system (e.g. Respecting the semantics of [`DllImportAttribute.SetLastError`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror)). + * No. There are rules and semantics that would be difficult to replicate with the current .NET API surface. Additional .NET APIs will likely need to be added in order to allow a Source Generator implementation to provide identical semantics with the built-in system (e.g. Respecting the semantics of [`DllImportAttribute.SetLastError`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror)). ## References @@ -214,10 +214,10 @@ namespace System.Runtime.InteropServices [IL Stubs description][il_stub_link] -[typemarshal_link]: https://docs.microsoft.com/dotnet/standard/native-interop/type-marshaling -[pinvoke_link]: https://docs.microsoft.com/dotnet/standard/native-interop/pinvoke +[typemarshal_link]: https://learn.microsoft.com/dotnet/standard/native-interop/type-marshaling +[pinvoke_link]: https://learn.microsoft.com/dotnet/standard/native-interop/pinvoke [comwrappers_link]: https://github.com/dotnet/runtime/issues/1845 [il_stub_link]: https://mattwarren.org/2019/09/26/Stubs-in-the-.NET-Runtime/ [source_gen_link]: https://github.com/dotnet/roslyn/blob/features/source-generators/docs/features/source-generators.md -[blittable_link]: https://docs.microsoft.com/dotnet/framework/interop/blittable-and-non-blittable-types +[blittable_link]: https://learn.microsoft.com/dotnet/framework/interop/blittable-and-non-blittable-types [il_linker_link]: https://github.com/dotnet/runtime/tree/main/src/tools/illink diff --git a/docs/design/features/standalone-gc-loading.md b/docs/design/features/standalone-gc-loading.md index 3c0b0e79be248..606b033ceb386 100644 --- a/docs/design/features/standalone-gc-loading.md +++ b/docs/design/features/standalone-gc-loading.md @@ -5,6 +5,10 @@ Author: Sean Gillespie (@swgillespie) - 2017 This document aims to provide a specification for how a standalone GC is to be loaded and what is to happen in the case of version mismatches. +**[Update June 2024] Warning** - The behavior described in this spec for how the CLR interprets environment variables +is out-of-date. See the [public documentation](https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#standalone-gc) +if you want up-to-date instructions on how to configure standalone GC. + ## Definitions Before diving in to the specification, it's useful to precisely define diff --git a/docs/design/features/unloadability.md b/docs/design/features/unloadability.md index 7ca316e0887b5..bc2e20b458d5e 100644 --- a/docs/design/features/unloadability.md +++ b/docs/design/features/unloadability.md @@ -29,7 +29,7 @@ ASP.NET (not Core) originally used AppDomains to support dynamic compiling and r ASP.NET Core moved to a more static model because of the lack of ability to unload stuff. Many of their customers did the same thing too. So, there is no pressing need for unloadability there at the moment. However, they use two tool that could potentially benefit from the unloadability -* Dotnet watch tool that watches a directory with sources and when a source file changes, it triggers recompilation and re-deployment. So, people can edit a source code used for their web page and the web server gets automatically recompiled and restarted. A need to restart the process negatively affects its performance. So, the ability to unload and reload just the modified assemblies without process restart would be very helpful here. See https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-2.1 for more details on the watch tool usage in ASP.NET Core. +* Dotnet watch tool that watches a directory with sources and when a source file changes, it triggers recompilation and re-deployment. So, people can edit a source code used for their web page and the web server gets automatically recompiled and restarted. A need to restart the process negatively affects its performance. So, the ability to unload and reload just the modified assemblies without process restart would be very helpful here. See https://learn.microsoft.com/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-2.1 for more details on the watch tool usage in ASP.NET Core. * Compilation server for Razor pages (on demand .cshtml files compilation to .dll). (https://github.com/aspnet/Razor/blob/master/src/Microsoft.AspNetCore.Razor.Tools/CompilerHost.cs) ### LINQPad LINQPad (https://www.linqpad.net) is a very popular third-party tool for designing LINQ queries against various data sources. It uses AppDomains and their unloading mechanism heavily for the following purposes: diff --git a/docs/design/features/y2038.md b/docs/design/features/y2038.md new file mode 100644 index 0000000000000..3ce613ab893ee --- /dev/null +++ b/docs/design/features/y2038.md @@ -0,0 +1,91 @@ +# Y2038 support in .NET 9 Linux arm32 + +In .NET 9, we addressed the Y2038 problem for Linux arm32. @mattheweshleman [noticed](https://github.com/dotnet/runtime/issues/96460) the issue early in the release cycle, and we also [hit](https://github.com/dotnet/runtime/issues/101444) this when our Linux arm32 builds started hitting OpenSSL-related errors on Ubuntu 24.04, the first Ubuntu version to [enable 64-bit `time_t` by default for Linux arm32](https://discourse.ubuntu.com/t/ubuntu-24-04-lts-noble-numbat-release-notes/39890#year-2038-support-for-the-armhf-architecture-5). This document describes our approach to Y2038 support. + +### Summary + +Our default posture for new .NET versions is to align with the latest Linux distribution first, and then work backwards to determine a reasonable compatibility story for older distributions. The .NET 9 Linux arm32 build requires glibc 2.35 and uses 64-bit `time_t` by default. We've made this change to align with Ubuntu 24.04. This change works on systems with 32-bit `time_t` but cannot represent times larger than 32 bits. We detect whether OpenSSL uses 32-bit `time_t` and fail predictably if the time being represented on the .NET side does not fit in 32 bits. + +### Y2038 in Linux + +Y2038 support at the [kernel](https://lwn.net/Articles/643234/) layer includes new versions of syscalls that use 64-bit versions of time_t. Userspace programs that use 32-bit time_t continue to work with this kernel. + +The next layer up is [glibc](https://sourceware.org/glibc/wiki/Y2038ProofnessDesign), which got Y2038 support in version 2.34. Like the kernel, glibc took a backwards-compatible approach. For every existing API symbol that used 32-bit time, glibc added a new 64-bit equivalent. The glibc headers provide a define `__TIME_BITS` that determines which versions of the time-related symbols are referenced at compile-time. For example, a call to glibc's `mktime` becomes a call to `__mktime64` when built with `__TIME_BITS==64`. + +However, the backwards compatibility ends with glibc. Linux distributions took on the project of building the remaining distribution-provided userspace libraries and applications with `__TIME_BITS=64`, introducing an ABI break at the boundary of any library with public surface that referenced `time_t`. + +Normally, ABI breeaks like this are not a problem for the Linux ecosystem because distributions rebuild all packages using the new ABI. However, the Microsoft distribution of .NET is somewhat unique in that it provides one set of binaries that are compatible with a broad range of distros (as long as the architecture and libc flavor match). This is similar to Python's [manylinux](https://github.com/pypa/manylinux) wheels. + +[Ubuntu](https://discourse.ubuntu.com/t/ubuntu-24-04-lts-noble-numbat-release-notes/39890#year-2038-support-for-the-armhf-architecture-5) made this change in 24.04, and [Debian](https://wiki.debian.org/ReleaseGoals/64bit-time) 13 is planned to be the first Debian release with 64-bit time. + +[musl](https://musl.libc.org/time64.html) version 1.2.0 got 64-bit time_t support, which was adopted in [Alpine](https://wiki.alpinelinux.org/wiki/Release_Notes_for_Alpine_3.13.0) 3.13. + +### .NET builds + +.NET official builds are produced by building the product on a [dedicated set of build images](https://github.com/dotnet/runtime/blob/main/docs/workflow/building/coreclr/linux-instructions.md). The images run Azure Linux 3.0 with an up-to-date cross-compilation toolchain, and also include a root filesystem of an old Linux distribution that provides an old version of glibc (or musl libc), which determines the libc compatibility of our builds. + +In .NET 8, we [support](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md#libc-compatibility) glibc 2.23 and musl 1.2.2, by building the product with a root filesystem from Ubuntu 16.04 and Alpine 3.13, respectively. + +### .NET Y2038 support + +.NET 8 already [supports](https://github.com/dotnet/runtime/pull/51099) 64-bit time on musl Linux, and on architectures other than 32-bit arm. This just leaves the glibc-based arm32 Linux build. + +#### glibc + +Because our build already [defined](https://github.com/dotnet/runtime/pull/100461) `_TIME_BITS=64`, the main change we had to make was to [update](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/pull/1037) the Ubuntu version of our rootfs to one that had a glibc with `_TIME_BITS` support. By targeting Ubuntu 22.04, .NET would be built with 64-bit `time_t`, allowing `DateTime.UtcNow` to [work](https://github.com/dotnet/runtime/issues/96460) correctly past Y2038. + +With this change, glibc-based .NET 9 (on Linux arm) is only [supported](https://github.com/dotnet/core/blob/main/release-notes/9.0/supported-os.md) on distributions with at least glibc 2.35 (from Ubuntu 22.04). + +#### OpenSSL + +Notice that we require a 64-bit time compatible glibc, _but_ the rest of the userspace may still be built with 32-bit time. This causes further problems when .NET interacts with other userspace libraries, in particular OpenSSL. An initial analysis [identified](https://github.com/dotnet/runtime/issues/101444) a couple of spots where .NET was passing `time_t` values (now 64-bit, with the aforementioned change) into OpenSSL functions. Before Ubuntu 24.04 and Debian 13, this meant we would be passing 64-bit values into functions that expected 32-bit values at two [callsites](https://github.com/dotnet/runtime/issues/101444#issuecomment-2077632497): + +- One was passing a time to OpenSSL's `X509_VERIFY_PARAM_set_time` for the purpose of validating a certificate chain. +- Another was calling `X509_cmp_time` to compare the current time with a time returned from an OCSP response, as part of getting the expiry time. + +We didn't want to cause a potential security hole by clamping a larger-than-32-bit time to `INT_MAX` or `INT_MIN`, as that might (for example) cause OpenSSL to accept a certificate chain valid during the Y2038 rollover, well past Y2038 (as it would internally be comparing an `ASN1_GENERALIZEDTIME` that does not have the Y2038 problem with a 32-bit time). + +Instead we wanted to detect whether we were running with an OpenSSL that used 32-bit `time_t`, and fail predictably if working with times that did not fit in 32 bits. + +We solved this by calling a simple OpenSSL function, `OPENSSL_gmtime`, that takes a pointer to a `time_t` and returns a broken-down time in the form of a `tm` struct. For this call we use an ABI trick: we pass in a 64-bit `time_t` as a pointer, and 32-bit `time_t` builds of OpenSSL will interpret the low 32 bits of the referenced value as a 32-bit `time_t` due to little-endianness. We then examine the broken-down time to see whether it represents the full 64-bit value (indicating that OpenSSL is using 64-bit `time_t`), or just the lower 32 bits. + +[Here](https://github.com/dotnet/runtime/pull/102410/files#diff-592e31e5115ea6d3235bc9a81ee765635da398589f1bd51d7d66bfbca814f1b6R236-R251S) is the interesting bit of code: + +```c + // This value will represent a time in year 2038 if 64-bit time is used, + // or 1901 if the lower 32 bits are interpreted as a 32-bit time_t value. + time_t timeVal = (time_t)INT_MAX + 1; + struct tm tmVal = { 0 }; + + // Detect whether openssl is using 32-bit or 64-bit time_t. + // If it uses 32-bit time_t, little-endianness means that the pointer + // will be interpreted as a pointer to the lower 32 bits of timeVal. + // tm_year is the number of years since 1900. + if (!OPENSSL_gmtime(&timeVal, &tmVal) || (tmVal.tm_year != 138 && tmVal.tm_year != 1)) + { + fprintf(stderr, "Cannot determine the time_t size used by libssl\n"); + abort(); + } + + g_libSslUses32BitTime = (tmVal.tm_year == 1); +``` + +Then at the other callsites, we just check if our value is larger than 32 bits, and fail early if it is, or do the right thing for the 32-bit version of OpenSSL if not, by calling versions of the OpenSSL functions that take 32-bit `time_t`. For [example](https://github.com/dotnet/runtime/pull/102410/files#diff-b144366bd4c3520d0793a1b06c10c5efb57f85f6bb68a1a9073f8dd0f1a0efa5R968-R976): + +```c + if (g_libSslUses32BitTime) + { + if (verifyTime > INT_MAX || verifyTime < INT_MIN) + { + return 0; + } + + // Cast to a signature that takes a 32-bit value for the time. + ((void (*)(X509_VERIFY_PARAM*, int32_t))(void*)(X509_VERIFY_PARAM_set_time))(verifyParams, (int32_t)verifyTime); + return 1; + } +``` + +This cast is ensuring that the call uses the correct ABI that the OpenSSL expects for the 32-bit `time_t` value. + +With that, we support running on 64-bit `time_t` compatible glibc, along with either 32-bit or 64-bit `time_t` OpenSSL, at least until we hit a time that does not fit into 32 bits. diff --git a/docs/design/libraries/LibraryImportGenerator/Compatibility.md b/docs/design/libraries/LibraryImportGenerator/Compatibility.md index 92f0c8e85e6a5..5105b6cc42ef7 100644 --- a/docs/design/libraries/LibraryImportGenerator/Compatibility.md +++ b/docs/design/libraries/LibraryImportGenerator/Compatibility.md @@ -51,15 +51,15 @@ In the event a marshaller would generate code that has a specific target framewo ### Semantic changes compared to `DllImportAttribute` -[`CharSet`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) has been replaced with a new `StringMarshalling` enumeration. `Ansi` and `Auto` are no longer supported as first-class options and `Utf8` has been added. +[`CharSet`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) has been replaced with a new `StringMarshalling` enumeration. `Ansi` and `Auto` are no longer supported as first-class options and `Utf8` has been added. -With `DllImportAttribute`, the default value of [`CharSet`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) is runtime/language-defined. In the built-in system, the default value of the `CharSet` property is `CharSet.Ansi`. The P/Invoke source generator makes no assumptions about `StringMarshalling` if it is not explicitly set on `LibraryImportAttribute`. Marshalling of `char` or `string` requires explicitly specifying marshalling information. +With `DllImportAttribute`, the default value of [`CharSet`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) is runtime/language-defined. In the built-in system, the default value of the `CharSet` property is `CharSet.Ansi`. The P/Invoke source generator makes no assumptions about `StringMarshalling` if it is not explicitly set on `LibraryImportAttribute`. Marshalling of `char` or `string` requires explicitly specifying marshalling information. -[`BestFitMapping`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.bestfitmapping) and [`ThrowOnUnmappableChar`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.throwonunmappablechar) will not be supported for `LibraryImportAttribute`. These values only have meaning on Windows when marshalling string data (`char`, `string`, `StringBuilder`) as [ANSI](https://docs.microsoft.com/windows/win32/intl/code-pages). As the general recommendation - including from Windows - is to move away from ANSI, the P/Invoke source generator will not support these fields. +[`BestFitMapping`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.bestfitmapping) and [`ThrowOnUnmappableChar`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.throwonunmappablechar) will not be supported for `LibraryImportAttribute`. These values only have meaning on Windows when marshalling string data (`char`, `string`, `StringBuilder`) as [ANSI](https://learn.microsoft.com/windows/win32/intl/code-pages). As the general recommendation - including from Windows - is to move away from ANSI, the P/Invoke source generator will not support these fields. -[`CallingConvention`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.callingconvention) will not be supported for `LibraryImportAttribute`. Users will be required to use the new `UnmanagedCallConvAttribute` attribute instead. This attribute provides support for extensible calling conventions and provides parity with the `UnmanagedCallersOnlyAttribute` attribute and C# function pointer syntax. We will enable our conversion code-fix to automatically convert explicit and known calling convention usage to use the `UnmanagedCallConvAttribute`. +[`CallingConvention`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.callingconvention) will not be supported for `LibraryImportAttribute`. Users will be required to use the new `UnmanagedCallConvAttribute` attribute instead. This attribute provides support for extensible calling conventions and provides parity with the `UnmanagedCallersOnlyAttribute` attribute and C# function pointer syntax. We will enable our conversion code-fix to automatically convert explicit and known calling convention usage to use the `UnmanagedCallConvAttribute`. -[`ExactSpelling`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.exactspelling) will not be supported for `LibraryImportAttribute`. If `ExactSpelling` is used on an existing `DllImport`, the offered code-fix will provide users with additional options for using `A` or `W` suffixed variants depending on the provided `CharSet` so they can explicitly choose which spelling is correct for their scenario. +[`ExactSpelling`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.exactspelling) will not be supported for `LibraryImportAttribute`. If `ExactSpelling` is used on an existing `DllImport`, the offered code-fix will provide users with additional options for using `A` or `W` suffixed variants depending on the provided `CharSet` so they can explicitly choose which spelling is correct for their scenario. ### Required references @@ -68,30 +68,30 @@ The following framework references are required: - `System.Runtime` - `System.Runtime.InteropServices` -These are all part of `NetCoreApp` and will be referenced by default unless [implicit framework references are disabled](https://docs.microsoft.com/dotnet/core/project-sdk/msbuild-props#disableimplicitframeworkreferences). +These are all part of `NetCoreApp` and will be referenced by default unless [implicit framework references are disabled](https://learn.microsoft.com/dotnet/core/project-sdk/msbuild-props#disableimplicitframeworkreferences). ### `char` marshalling Marshalling of `char` will only be supported with `StringMarshalling.Utf16` or as `UnmanagedType.U2` or `UnmanagedType.I2`. It will not be supported when configured with any of the following: - - [`UnmanagedType.U1` or `UnmanagedType.I1`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) + - [`UnmanagedType.U1` or `UnmanagedType.I1`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) - `StringMarshalling.Utf8` will not be supported. - - No explicit marshalling information - either `LibraryImportAttribute.StringMarshalling` or [`MarshalAsAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute) + - No explicit marshalling information - either `LibraryImportAttribute.StringMarshalling` or [`MarshalAsAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute) -In the built-in system, marshalling with `CharSet.Ansi` and `CharSet.None` used the [system default Windows ANSI code page](https://docs.microsoft.com/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte) when on Windows and took the first byte of the UTF-8 encoding on non-Windows platforms. The above reasoning also applies to marshalling of a `char` as `UnmanagedType.U1` and `UnmanagedType.I1`. All approaches are fundamentally flawed and therefore not supported. If a single-byte character is expected to be marshalled it is left to the caller to convert a .NET `char` into a single `byte` prior to calling the native function. +In the built-in system, marshalling with `CharSet.Ansi` and `CharSet.None` used the [system default Windows ANSI code page](https://learn.microsoft.com/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte) when on Windows and took the first byte of the UTF-8 encoding on non-Windows platforms. The above reasoning also applies to marshalling of a `char` as `UnmanagedType.U1` and `UnmanagedType.I1`. All approaches are fundamentally flawed and therefore not supported. If a single-byte character is expected to be marshalled it is left to the caller to convert a .NET `char` into a single `byte` prior to calling the native function. For `CharSet.Auto`, the built-in system relied upon detection at runtime of the platform when determining the targeted encoding. Performing this check in generated code violates the "pay-for-play" principle. Given that there are no scenarios for this feature in `NetCoreApp` it will not be supported. ### `string` marshalling Marshalling of `string` will not be supported when configured with any of the following: - - [`UnmanagedType.VBByRefStr`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) - - No explicit marshalling information - either `LibraryImportAttribute.StringMarshalling` or [`MarshalAsAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute) + - [`UnmanagedType.VBByRefStr`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) + - No explicit marshalling information - either `LibraryImportAttribute.StringMarshalling` or [`MarshalAsAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute) -When converting from native to managed, the built-in system would throw a [`MarshalDirectiveException`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshaldirectiveexception) if the string's length is over 0x7ffffff0. The generated marshalling code will no longer perform this check. +When converting from native to managed, the built-in system would throw a [`MarshalDirectiveException`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshaldirectiveexception) if the string's length is over 0x7ffffff0. The generated marshalling code will no longer perform this check. -In the built-in system, marshalling a `string` contains an optimization for parameters passed by value to allocate on the stack (instead of through [`AllocCoTaskMem`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if the string is below a certain length (MAX_PATH). For UTF-16, this optimization was also applied for parameters passed by read-only reference. The generated marshalling code will include this optimization for read-only reference parameters for non-UTF-16 as well. +In the built-in system, marshalling a `string` contains an optimization for parameters passed by value to allocate on the stack (instead of through [`AllocCoTaskMem`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if the string is below a certain length (MAX_PATH). For UTF-16, this optimization was also applied for parameters passed by read-only reference. The generated marshalling code will include this optimization for read-only reference parameters for non-UTF-16 as well. -When marshalling as [ANSI](https://docs.microsoft.com/windows/win32/intl/code-pages) on Windows (using `UnmanagedType.LPStr`): +When marshalling as [ANSI](https://learn.microsoft.com/windows/win32/intl/code-pages) on Windows (using `UnmanagedType.LPStr`): - Best-fit mapping will be disabled and no exception will be thrown for unmappable characters. In the built-in system, this behaviour was configured through [`DllImportAttribute.BestFitMapping`] and [`DllImportAttribute.ThrowOnUnmappableChar`]. The generated marshalling code will have the equivalent behaviour of `BestFitMapping=false` and `ThrowOnUnmappableChar=false`. The p/invoke source generator does not provide an equivalent to using `CharSet.Auto` in the built-in system. If platform-dependent behaviour is desired, it is left to the user to define different p/invokes with different marshalling configurations. @@ -104,27 +104,27 @@ To aid in conversion from `DllImport` to source-generated marshalling, the code- ### Custom marshaller support -Using a custom marshaller (i.e. [`ICustomMarshaler`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler)) with the `UnmanagedType.CustomMarshaler` value on `MarshalAsAttribute` is not supported. This also implies `MarshalAsAttribute` fields: [`MarshalTypeRef`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshaltyperef), [`MarshalType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshaltype), and [`MarshalCookie`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshalcookie) are unsupported. +Using a custom marshaller (i.e. [`ICustomMarshaler`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler)) with the `UnmanagedType.CustomMarshaler` value on `MarshalAsAttribute` is not supported. This also implies `MarshalAsAttribute` fields: [`MarshalTypeRef`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshaltyperef), [`MarshalType`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshaltype), and [`MarshalCookie`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.marshalcookie) are unsupported. ### Array marshalling -Marshalling of arrays will not be supported when using [`UnmanagedType.SafeArray`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype). This implies that the following `MarshalAsAttribute` fields are unsupported: [`SafeArraySubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearraysubtype) and [`SafeArrayUserDefinedSubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearrayuserdefinedsubtype) +Marshalling of arrays will not be supported when using [`UnmanagedType.SafeArray`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype). This implies that the following `MarshalAsAttribute` fields are unsupported: [`SafeArraySubType`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearraysubtype) and [`SafeArrayUserDefinedSubType`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearrayuserdefinedsubtype) -Specifying array-specific marshalling members on the `MarshalAsAttribute` such as [`SizeConst`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeconst), [`ArraySubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.arraysubtype), and [`SizeParamIndex`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeparamindex) with non-array `UnmanagedType` types is unsupported. +Specifying array-specific marshalling members on the `MarshalAsAttribute` such as [`SizeConst`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeconst), [`ArraySubType`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.arraysubtype), and [`SizeParamIndex`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeparamindex) with non-array `UnmanagedType` types is unsupported. Only single-dimensional arrays are supported for source generated marshalling. -In the source-generated marshalling, arrays will be allocated on the stack (instead of through [`AllocCoTaskMem`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if they are passed by value or by read-only reference if they contain at most 256 bytes of data. The built-in system does not support this optimization for arrays. +In the source-generated marshalling, arrays will be allocated on the stack (instead of through [`AllocCoTaskMem`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if they are passed by value or by read-only reference if they contain at most 256 bytes of data. The built-in system does not support this optimization for arrays. In the built-in system, marshalling a `char` array by value with `CharSet.Unicode` would default to also marshalling data out. In the source-generated marshalling, the `char` array must be marked with the `[Out]` attribute for data to be marshalled out by value. ### `in` keyword -For some types - blittable or Unicode `char` - passed by read-only reference via the [`in` keyword](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/in-parameter-modifier), the built-in system simply pins the parameter. The generated marshalling code does the same, such that there is no behavioural difference. A consequence of this behaviour is that any modifications made by the invoked function will be reflected in the caller. It is left to the user to avoid the situation in which `in` is used for a parameter that will actually be modified by the invoked function. +For some types - blittable or Unicode `char` - passed by read-only reference via the [`in` keyword](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/in-parameter-modifier), the built-in system simply pins the parameter. The generated marshalling code does the same, such that there is no behavioural difference. A consequence of this behaviour is that any modifications made by the invoked function will be reflected in the caller. It is left to the user to avoid the situation in which `in` is used for a parameter that will actually be modified by the invoked function. ### `LCIDConversion` support -[`LCIDConversionAttribute`](`https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.lcidconversionattribute`) will not be supported for methods marked with `LibraryImportAttribute`. +[`LCIDConversionAttribute`](`https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.lcidconversionattribute`) will not be supported for methods marked with `LibraryImportAttribute`. ### `[In, Out]` Attributes @@ -137,11 +137,11 @@ Support for struct marshalling in the source-generated marshalling is described ### Unsupported types Unlike the built-in system, the source generator does not support marshalling for the following types: -- [`CriticalHandle`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.criticalhandle) -- [`HandleRef`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.handleref) -- [`StringBuilder`](https://docs.microsoft.com/dotnet/api/system.text.stringbuilder) +- [`CriticalHandle`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.criticalhandle) +- [`HandleRef`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.handleref) +- [`StringBuilder`](https://learn.microsoft.com/dotnet/api/system.text.stringbuilder) -The source generator also does not support marshalling objects using the following [`UnmanagedType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) values: +The source generator also does not support marshalling objects using the following [`UnmanagedType`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) values: - `UnmanagedType.Interface` - `UnmanagedType.IDispatch` - `UnmanagedType.IInspectable` diff --git a/docs/design/libraries/LibraryImportGenerator/Pipeline.md b/docs/design/libraries/LibraryImportGenerator/Pipeline.md index bd38953951751..9414e8d51b636 100644 --- a/docs/design/libraries/LibraryImportGenerator/Pipeline.md +++ b/docs/design/libraries/LibraryImportGenerator/Pipeline.md @@ -8,11 +8,11 @@ The P/Invoke source generator is responsible for finding all methods marked with 1. [Generate the corresponding P/Invoke](#pinvoke) 1. Add the generated source to the compilation. -The pipeline uses the Roslyn [Syntax APIs](https://docs.microsoft.com/dotnet/api/microsoft.codeanalysis.csharp.syntax) to create the generated code. This imposes some structure for the marshalling generators and allows for easier inspection or modification (if desired) of the generated code. +The pipeline uses the Roslyn [Syntax APIs](https://learn.microsoft.com/dotnet/api/microsoft.codeanalysis.csharp.syntax) to create the generated code. This imposes some structure for the marshalling generators and allows for easier inspection or modification (if desired) of the generated code. ## Symbol and metadata processing -The generator processes the method's `LibraryImportAttribute` data, the method's parameter and return types, and the metadata on them (e.g. [`LCIDConversionAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.lcidconversionattribute), [`MarshalAsAttribute`][MarshalAsAttribute], [struct marshalling attributes](StructMarshalling.md)). This information is used to determine the corresponding native type for each managed parameter/return type and how they will be marshalled. +The generator processes the method's `LibraryImportAttribute` data, the method's parameter and return types, and the metadata on them (e.g. [`LCIDConversionAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.lcidconversionattribute), [`MarshalAsAttribute`][MarshalAsAttribute], [struct marshalling attributes](StructMarshalling.md)). This information is used to determine the corresponding native type for each managed parameter/return type and how they will be marshalled. A [`TypePositionInfo`][src-TypePositionInfo] is created for each type that needs to be marshalled. For each parameter and return type, this captures the managed type, managed and native positions (return or index in parameter list), and marshalling information. @@ -154,7 +154,7 @@ To help enable developers to use the full model described in the [Struct Marshal ### `SetLastError=true` -The stub code generation also handles [`SetLastError=true`][SetLastError] behaviour. This configuration indicates that system error code ([`errno`](https://en.wikipedia.org/wiki/Errno.h) on Unix, [`GetLastError`](https://docs.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) on Windows) should be stored after the native invocation, such that it can be retrieved using [`Marshal.GetLastWin32Error`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error). +The stub code generation also handles [`SetLastError=true`][SetLastError] behaviour. This configuration indicates that system error code ([`errno`](https://en.wikipedia.org/wiki/Errno.h) on Unix, [`GetLastError`](https://learn.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) on Windows) should be stored after the native invocation, such that it can be retrieved using [`Marshal.GetLastWin32Error`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error). This means that, rather than simply invoke the native method, the generated stub will: @@ -229,16 +229,16 @@ static partial byte Method__PInvoke__(ushort* s); [src-StubCodeContext]: /src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs [src-TypePositionInfo]: /src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs -[DllImportAttribute]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute -[MarshalAsAttribute]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute -[InAttribute]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.inattribute -[OutAttribute]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.outattribute - -[BestFitMapping]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.bestfitmapping -[CallingConvention]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.callingconvention -[CharSet]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset -[EntryPoint]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.entrypoint -[ExactSpelling]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.exactspelling -[PreserveSig]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig -[SetLastError]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror -[ThrowOnUnmappableChar]: https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.throwonunmappablechar +[DllImportAttribute]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute +[MarshalAsAttribute]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute +[InAttribute]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.inattribute +[OutAttribute]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.outattribute + +[BestFitMapping]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.bestfitmapping +[CallingConvention]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.callingconvention +[CharSet]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset +[EntryPoint]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.entrypoint +[ExactSpelling]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.exactspelling +[PreserveSig]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig +[SetLastError]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror +[ThrowOnUnmappableChar]: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.throwonunmappablechar diff --git a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md index 24bc5dbb4a75a..0d285f9287062 100644 --- a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md +++ b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md @@ -282,7 +282,7 @@ The P/Invoke source generator (as well as the struct source generator when neste If a structure type does not meet the requirements to not require marshalling or does not have the `NativeMarshallingAttribute` applied at the type definition, the user can supply a `MarshalUsingAttribute` at the marshalling location (field, parameter, or return value) with a native type matching the same requirements as `NativeMarshallingAttribute`'s native type. -All generated stubs will be marked with [`SkipLocalsInitAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.compilerservices.skiplocalsinitattribute) on supported frameworks. This does require attention when performing custom marshalling as the state of stub allocated memory will be in an undefined state. +All generated stubs will be marked with [`SkipLocalsInitAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.skiplocalsinitattribute) on supported frameworks. This does require attention when performing custom marshalling as the state of stub allocated memory will be in an undefined state. ### Special case: Transparent Structures diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md index 2268d81bf1047..6ad08fc09c8da 100644 --- a/docs/design/mono/debugger.md +++ b/docs/design/mono/debugger.md @@ -2,7 +2,7 @@ ## Overview -The details of launching a Debugger session for a Blazor WebAssembly application is described [here](https://docs.microsoft.com/en-us/aspnet/core/blazor/debug?view=aspnetcore-6.0&tabs=visual-studio). +The details of launching a Debugger session for a Blazor WebAssembly application is described [here](https://learn.microsoft.com/aspnet/core/blazor/debug?view=aspnetcore-6.0&tabs=visual-studio). ## Debugger Attributes Web Assembly Debugger supports usage of following attributes: @@ -18,7 +18,7 @@ Web Assembly Debugger supports usage of following attributes: - Stepping In/Over: results in an additional stepping need to proceed to the next line.

- __System.Diagnostics.DebuggerDisplay__ - __System.Diagnostics.DebuggerTypeProxy__ -- __System.Diagnostics.DebuggerBrowsable__ ([doc](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggerbrowsableattribute?view=net-6.0)) +- __System.Diagnostics.DebuggerBrowsable__ ([doc](https://learn.microsoft.com/dotnet/api/system.diagnostics.debuggerbrowsableattribute?view=net-6.0)) - Collapsed - displayed normally. - RootHidden: - Simple type - not displayed in the debugger window. diff --git a/docs/design/mono/diagnostics-tracing.md b/docs/design/mono/diagnostics-tracing.md index 9d1f668871cae..898ea1a3a96ff 100644 --- a/docs/design/mono/diagnostics-tracing.md +++ b/docs/design/mono/diagnostics-tracing.md @@ -19,13 +19,13 @@ Existing diagnostic tooling only supports `NamedPipes`/`UnixDomainSockets`, so i For more details around diagnostic scenarios, see: -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters +https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-counters -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace +https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/event-counter-perf +https://learn.microsoft.com/dotnet/core/diagnostics/event-counter-perf -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/debug-highcpu +https://learn.microsoft.com/dotnet/core/diagnostics/debug-highcpu ## Building an application including diagnostic tracing support @@ -380,7 +380,7 @@ NOTE, iOS only support use of loopback interface when running DiagnosticServer i ### Application running single file based EventPipe session -If application supports controlled runtime shutdown, `mono_jit_cleanup` gets called before terminating process, it is possible to run a single file based EventPipe session using environment variables as described in https://docs.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables. In .net6 an additional variable has been added, `DOTNET_EventPipeOutputStreaming`, making sure data is periodically flushed into the output file. +If application supports controlled runtime shutdown, `mono_jit_cleanup` gets called before terminating process, it is possible to run a single file based EventPipe session using environment variables as described in https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#trace-using-environment-variables. In .net6 an additional variable has been added, `DOTNET_EventPipeOutputStreaming`, making sure data is periodically flushed into the output file. If application doesn't support controlled runtime shutdown, this mode won't work, since it requires rundown events, only emitted when closing session and flushing memory manager. If application doesn't call `mono_jit_cleanup` before terminating, generated nettrace file will lack rundown events needed to produce callstacks including symbols. @@ -493,9 +493,9 @@ Combining different sessions including different GC information opens up ability Collected events retrieved over EventPipe sessions is stored in a nettrace file that can be analyzed using tooling like PerfView, Speedscope, Chronium or Visual Studio: -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/debug-highcpu?tabs=windows#trace-generation +https://learn.microsoft.com/dotnet/core/diagnostics/debug-highcpu?tabs=windows#trace-generation -https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace#dotnet-trace-convert +https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace#dotnet-trace-convert https://github.com/dotnet/diagnostics/blob/main/documentation/tutorial/app_running_slow_highcpu.md diff --git a/docs/design/mono/mono-library-mode.md b/docs/design/mono/mono-library-mode.md index 42288fa22a447..f6c6a98e5a746 100644 --- a/docs/design/mono/mono-library-mode.md +++ b/docs/design/mono/mono-library-mode.md @@ -3,16 +3,16 @@ Library Mode on Mono # Background -For many native applications, accessibility to bountiful APIs from .NET runtime libraries can save developers from "reinventing the wheel" in the target platform's native language. That is where [interoperability](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/interop/) comes in handy to access modern .NET APIs from the native side. The .NET runtime libraries require the .NET runtime to function properly, and integrating the entire .NET ecosystem may prove cumbersome and unnecessary. Instead, for a smaller footprint and more seamless experience, the runtime and custom managed code invoking .NET APIs can be bundled into a library for direct consumption. In line with [Native code interop with Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/interop), as of .NET 8, the [mono runtime supports a library mode](https://github.com/dotnet/runtime/issues/79377) enabling mobile developers to leverage modern .NET APIs in their mobile applications with a single static or shared library. +For many native applications, accessibility to bountiful APIs from .NET runtime libraries can save developers from "reinventing the wheel" in the target platform's native language. That is where [interoperability](https://learn.microsoft.com/dotnet/csharp/advanced-topics/interop/) comes in handy to access modern .NET APIs from the native side. The .NET runtime libraries require the .NET runtime to function properly, and integrating the entire .NET ecosystem may prove cumbersome and unnecessary. Instead, for a smaller footprint and more seamless experience, the runtime and custom managed code invoking .NET APIs can be bundled into a library for direct consumption. In line with [Native code interop with Native AOT](https://learn.microsoft.com/dotnet/core/deploying/native-aot/interop), as of .NET 8, the [mono runtime supports a library mode](https://github.com/dotnet/runtime/issues/79377) enabling mobile developers to leverage modern .NET APIs in their mobile applications with a single static or shared library. Note: The library generated from Mono's Library Mode containing custom managed code and the mono runtime will, for brevity, be referred to as the mono library. # How it works The core components of mono's library mode that enables interoperability between native and managed code are as follows: -1. [UnmanagedCallersOnlyAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-7.0) which allows native code to directly call managed methods. -2. [Direct Platform Invoke (P/Invoke)](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke) which allows managed code to directly call native functions. -3. The mono runtime which facilitates the above interop directions among its other responsibilities as a [managed runtime](https://learn.microsoft.com/en-us/dotnet/core/introduction#runtime). +1. [UnmanagedCallersOnlyAttribute](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-7.0) which allows native code to directly call managed methods. +2. [Direct Platform Invoke (P/Invoke)](https://learn.microsoft.com/dotnet/standard/native-interop/pinvoke) which allows managed code to directly call native functions. +3. The mono runtime which facilitates the above interop directions among its other responsibilities as a [managed runtime](https://learn.microsoft.com/dotnet/core/introduction#runtime). Being able to call managed .NET APIs from a native application has many usecases, including reducing the need to rewrite logic in the native language when there is no native counterpart. In order to call managed code leveraging these .NET APIs, the native application needs to recognize the corresponding symbols. Once custom managed code is compiled into managed assemblies, the Mono AOT Compiler processes them to generate [native-to-managed wrappers](https://github.com/dotnet/runtime/blob/43d164d8d65d163fef0de185eb11cfa0b1291919/src/mono/mono/mini/aot-compiler.c#L5446-L5498) for all methods decorated with [`UnmanagedCallersOnlyAttribute`](https://github.com/dotnet/runtime/pull/79424). These native-to-managed wrappers have entrypoint symbols specified by the corresponding `UnmanagedCallersOnlyAttribute`, allowing native code to call them directly. So once the mono library is linked/loaded into the native application, the Mono AOT Compiled assemblies should be [preloaded by the mono runtime](https://github.com/dotnet/runtime/blob/43d164d8d65d163fef0de185eb11cfa0b1291919/src/tasks/LibraryBuilder/Templates/preloaded-assemblies.c#L10) once the mono runtime is initialized in order to enable calling managed methods from the native side of the application. diff --git a/docs/design/mono/profiled-aot.md b/docs/design/mono/profiled-aot.md index 80e36b9e9c02b..2d88a7c9ea584 100644 --- a/docs/design/mono/profiled-aot.md +++ b/docs/design/mono/profiled-aot.md @@ -9,9 +9,9 @@ Mobile applications built using .NET typically leverage the Mono runtime to load The advantages of Profiled AOT stem from its flexibility to AOT select code paths, leaving the rest to be compiled on the fly by the JIT compiler or Mono Interpreter. With an analysis of an application's trace, a record capturing a sequence of events during the application's execution, profiles can be generated to tailor optimizations for each application and environment. For example, profiles may target frequently executed (hot) code paths, minimizing the amount of runtime compilations and reducing application size, which are especially important in environments where full AOT compilation would strain storage space. Moreover, profiles may target startup code to optimize startup performance. -Within .NET, traces can be collected by [diagnostic tooling](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe#tools-that-use-eventpipe) that use the [EventPipe](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe) runtime component. [Existing diagnostic tooling only supports `NamedPipes`/`UnixDomainSockets`](https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md), so the [diagnostics tool dotnet-dsrouter](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dsrouter) is required to bridge the EventPipe-based diagnostic tooling with .NET applications on mobile platforms and other remote sandboxed environments. +Within .NET, traces can be collected by [diagnostic tooling](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe#tools-that-use-eventpipe) that use the [EventPipe](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe) runtime component. [Existing diagnostic tooling only supports `NamedPipes`/`UnixDomainSockets`](https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md), so the [diagnostics tool dotnet-dsrouter](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter) is required to bridge the EventPipe-based diagnostic tooling with .NET applications on mobile platforms and other remote sandboxed environments. -Events collected by EventPipe-based diagnostic tooling are emitted with a [`.nettrace` file format](https://github.com/microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md). Among the [various events supported by Mono runtime](https://github.com/dotnet/runtime/blob/main/src/mono/mono/eventpipe/gen-eventing-event-inc.lst), [method jitting and method loading](https://github.com/dotnet/runtime/blob/096b2499fe6939d635c35edaa607a180eb578fbb/src/mono/mono/eventpipe/gen-eventing-event-inc.lst#L39-L41) are crucial to [inform the Mono AOT Compiler what methods to AOT](https://github.com/dotnet/runtime/blob/6b67caaedfbfeaf7707478e50ccc9e8bc929e591/src/mono/mono/mini/aot-compiler.c#L13818-L13880). To collect a trace containing such events, it is imperative that dotnet-trace is provided either the appropriate [event provider](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/well-known-event-providers) with [keyword flags](https://github.com/dotnet/runtime/blob/c59aef7622c9a2499abb1b7d262ed0c90f4b0c7f/src/coreclr/vm/ClrEtwAll.man#L14-L92) through `--providers` or the appropriate list of keywords through `--clrevents`. That way, the [events relevant to the keywords are captured](https://github.com/dotnet/runtime/blob/c59aef7622c9a2499abb1b7d262ed0c90f4b0c7f/src/coreclr/vm/ClrEtwAll.man#L3133). In the example workflows below, `--providers Microsoft-Windows-DotNETRuntime:0x1F000080018:5` is used. +Events collected by EventPipe-based diagnostic tooling are emitted with a [`.nettrace` file format](https://github.com/microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md). Among the [various events supported by Mono runtime](https://github.com/dotnet/runtime/blob/main/src/mono/mono/eventpipe/gen-eventing-event-inc.lst), [method jitting and method loading](https://github.com/dotnet/runtime/blob/096b2499fe6939d635c35edaa607a180eb578fbb/src/mono/mono/eventpipe/gen-eventing-event-inc.lst#L39-L41) are crucial to [inform the Mono AOT Compiler what methods to AOT](https://github.com/dotnet/runtime/blob/6b67caaedfbfeaf7707478e50ccc9e8bc929e591/src/mono/mono/mini/aot-compiler.c#L13818-L13880). To collect a trace containing such events, it is imperative that dotnet-trace is provided either the appropriate [event provider](https://learn.microsoft.com/dotnet/core/diagnostics/well-known-event-providers) with [keyword flags](https://github.com/dotnet/runtime/blob/c59aef7622c9a2499abb1b7d262ed0c90f4b0c7f/src/coreclr/vm/ClrEtwAll.man#L14-L92) through `--providers` or the appropriate list of keywords through `--clrevents`. That way, the [events relevant to the keywords are captured](https://github.com/dotnet/runtime/blob/c59aef7622c9a2499abb1b7d262ed0c90f4b0c7f/src/coreclr/vm/ClrEtwAll.man#L3133). In the example workflows below, `--providers Microsoft-Windows-DotNETRuntime:0x1F000080018:5` is used. Profiles [ingested by the Mono AOT Compiler](https://github.com/dotnet/runtime/blob/6b67caaedfbfeaf7707478e50ccc9e8bc929e591/src/tasks/AotCompilerTask/MonoAOTCompiler.cs#L174) are generated through .NET runtime's [`dotnet-pgo` tool](https://github.com/dotnet/runtime/blob/main/docs/design/features/dotnet-pgo.md). As such, profiles passed to the Mono AOT Compiler are expected to adhere to the [`.mibc` file format](https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/dotnet-pgo/dotnet-pgo-experiment.md#mibc-file-format). The Mono AOT Compiler [reads `.mibc` profiles](https://github.com/dotnet/runtime/blob/c59aef7622c9a2499abb1b7d262ed0c90f4b0c7f/src/mono/mono/mini/aot-compiler.c#L14085-L14162) to determine [which methods to AOT](https://github.com/dotnet/runtime/blob/6b67caaedfbfeaf7707478e50ccc9e8bc929e591/src/mono/mono/mini/aot-compiler.c#L13818-L13880) when compiling CIL assemblies. @@ -34,8 +34,8 @@ As the [`.mibc` profile](https://github.com/dotnet/runtime/blob/main/src/coreclr ### Requirements: - [Prerequisites](https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/libraries/testing-android.md#prerequisites) - [Building mono and libs](https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/libraries/testing-android.md#building-libs-and-tests-for-android) -- [dotnet-dsrouter](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dsrouter) -- [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) +- [dotnet-dsrouter](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dsrouter) +- [dotnet-trace](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace) ### Tracing (if not using provided .nettrace/.mibc files) (informational) [Understanding diagnostics_tracing runtime component](https://github.com/dotnet/runtime/blob/main/docs/design/mono/diagnostics-tracing.md) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index e9722759ea80e..f6b8f9b964eff 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1069,7 +1069,7 @@ Note about creating zero-based, one-dimensional arrays in section III.4.21 "newo ## API documentation -API documentation included in partition IV: Profiles and Libraries is superseded by the actively maintained API documentation in https://github.com/dotnet/dotnet-api-docs repo. The documentation is published at https://docs.microsoft.com/en-us/dotnet/api/. +API documentation included in partition IV: Profiles and Libraries is superseded by the actively maintained API documentation in https://github.com/dotnet/dotnet-api-docs repo. The documentation is published at https://learn.microsoft.com/dotnet/api/. The incorrect description of `System.Array.Initialize` API in section "II.13.2 Initializing value types" is replaced with "The Base Class Library provides the method System.Array.Initialize (see Partition IV) to initialize every element of an array of unboxed value types by calling its parameterless instance constructor." diff --git a/docs/design/tools/illink/trimmed-assemblies.md b/docs/design/tools/illink/trimmed-assemblies.md index bf2b9273d4fc5..ac8e03fddc2aa 100644 --- a/docs/design/tools/illink/trimmed-assemblies.md +++ b/docs/design/tools/illink/trimmed-assemblies.md @@ -66,7 +66,7 @@ This shows how Blazor (or a developer) can hook into the build to opt assemblies ### Other options -.NET 5 introduced a host of additional SDK options that map directly to the underlying illink options. The full list is documented at https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options. +.NET 5 introduced a host of additional SDK options that map directly to the underlying illink options. The full list is documented at https://learn.microsoft.com/dotnet/core/deploying/trimming-options. ## .NET 6 @@ -100,7 +100,7 @@ This ItemGroup contains assembly names that get opted into trimming via `IsTrimm ``` -The above opts `MyAssembly.dll` into trimming. Note that the ItemGroup should contain assembly names without an extension, similar to [`TrimmerRootAssembly`](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options#root-assemblies). Before .NET 6 this would have been done with a target: +The above opts `MyAssembly.dll` into trimming. Note that the ItemGroup should contain assembly names without an extension, similar to [`TrimmerRootAssembly`](https://learn.microsoft.com/dotnet/core/deploying/trimming-options#root-assemblies). Before .NET 6 this would have been done with a target: ```xml ``` -(Documentation for configuring feeds is [here](https://docs.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior).) +(Documentation for configuring feeds is [here](https://learn.microsoft.com/nuget/consume-packages/configuring-nuget-behavior).) ## Setup the project diff --git a/docs/project/glossary.md b/docs/project/glossary.md index 8e1de1bef9646..55d59730bd572 100644 --- a/docs/project/glossary.md +++ b/docs/project/glossary.md @@ -15,25 +15,25 @@ terminology. | BBT | Microsoft internal early version of C/C++ PGO. See https://www.microsoft.com/windows/cse/bit_projects.mspx. | | BOTR | Book Of The Runtime. | | BCL | Base Class Library. A set of `System.*` (and to a limited extent `Microsoft.*`) libraries that make up the lower layer of the .NET library stack. | -| CIL | Common Intermediate Language. Equivalent to IL, also equivalent to [MSIL](https://docs.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil). | +| CIL | Common Intermediate Language. Equivalent to IL, also equivalent to [MSIL](https://learn.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil). | | CLI | Command Line Interface, or Common Language Infastructure. | -| CLR | [Common Language Runtime](https://docs.microsoft.com/dotnet/standard/clr). | +| CLR | [Common Language Runtime](https://learn.microsoft.com/dotnet/standard/clr). | | COMPlus | An early name for the .NET platform, back when it was envisioned as a successor to the COM platform (hence, "COM+"). Used in various places in the CLR infrastructure, most prominently as a common prefix for the names of internal configuration settings. Note that this is different from the product that eventually ended up being named [COM+](https://msdn.microsoft.com/library/windows/desktop/ms685978.aspx). | | COR | [Common Object Runtime](http://www.danielmoth.com/Blog/mscorlibdll.aspx). The name of .NET before it was named .NET. | | CoreFX | Core Framework. Original project name for open source and cross-platform version of [.NET runtime libraries](https://github.com/dotnet/runtime/tree/main/src/libraries) | | DAC | Data Access Component. An abstraction layer over the internal structures in the runtime. | -| EE | [Execution Engine](https://docs.microsoft.com/dotnet/standard/managed-execution-process#running_code). | +| EE | [Execution Engine](https://learn.microsoft.com/dotnet/standard/managed-execution-process#running_code). | | GC | [Garbage Collector](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/garbage-collection.md). | | IBC | Instrumented Block Counts - used as extension (`*.ibc`) for old PGO files. | | IJW | "It Just Works" - Codename for [C++/CLI](https://learn.microsoft.com/cpp/dotnet/dotnet-programming-with-cpp-cli-visual-cpp) managed/native interop | | IPC | Inter-Process Communication. | -| IL | Intermediate Language. Equivalent to CIL, also equivalent to [MSIL](https://docs.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil). | +| IL | Intermediate Language. Equivalent to CIL, also equivalent to [MSIL](https://learn.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil). | | JIT | [Just-in-Time](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/jit/ryujit-overview.md) compiler. RyuJIT is the code name for the next generation Just-in-Time(aka "JIT") for the .NET runtime. | | LCG | Lightweight Code Generation. An early name for [dynamic methods](https://github.com/dotnet/runtime/blob/main/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs). | | MD | MetaData. | -| MDA | Managed Debugging Assistant - see [details](https://docs.microsoft.com/dotnet/framework/debug-trace-profile/diagnosing-errors-with-managed-debugging-assistants) (Note: Not in .NET Core, equivalent diagnostic functionality is made available on a case-by-case basis, e.g. [#9418](https://github.com/dotnet/runtime/issues/9418)) | +| MDA | Managed Debugging Assistant - see [details](https://learn.microsoft.com/dotnet/framework/debug-trace-profile/diagnosing-errors-with-managed-debugging-assistants) (Note: Not in .NET Core, equivalent diagnostic functionality is made available on a case-by-case basis, e.g. [#9418](https://github.com/dotnet/runtime/issues/9418)) | | MIBC | Managed Instrumented Block Counts - used as extension (`*.mibc`) for managed PGO files. | -| MSIL | [Microsoft Intermediate Language](https://docs.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil).Common Intermediate Language. Equivalent to IL, also equivalent to CIL. | +| MSIL | [Microsoft Intermediate Language](https://learn.microsoft.com/dotnet/standard/managed-execution-process#compiling-to-msil).Common Intermediate Language. Equivalent to IL, also equivalent to CIL. | | NGen | Native Image Generator. | | NYI | Not Yet Implemented. | | PAL | [Platform Adaptation Layer](http://archive.oreilly.com/pub/a/dotnet/2002/03/04/rotor.html). Provides an abstraction layer between the runtime and the operating system. | @@ -43,17 +43,17 @@ terminology. | ProjectN | Codename for the first version of [.NET Native for UWP](https://msdn.microsoft.com/vstudio/dotnetnative.aspx). | | R2R | Ready-to-Run. A flavor of native images - command line switch of [crossgen](../workflow/building/coreclr/crossgen.md). | | Redhawk | Codename for experimental minimal managed code runtime that evolved into [CoreRT](https://github.com/dotnet/corert/). | -| SDK | Software Development Kit. The [.NET SDK](https://docs.microsoft.com/dotnet/core/sdk) contains the .NET CLI, .NET libraries and runtime, and the dotnet driver. | -| SEH | [Structured Exception Handling](https://docs.microsoft.com/windows/win32/debug/structured-exception-handling). Unified mechanism for handling hardware and software exceptions on Windows. | +| SDK | Software Development Kit. The [.NET SDK](https://learn.microsoft.com/dotnet/core/sdk) contains the .NET CLI, .NET libraries and runtime, and the dotnet driver. | +| SEH | [Structured Exception Handling](https://learn.microsoft.com/windows/win32/debug/structured-exception-handling). Unified mechanism for handling hardware and software exceptions on Windows. | | SOS | [Son of Strike](https://learn.microsoft.com/dotnet/framework/tools/sos-dll-sos-debugging-extension). The debugging extension for DbgEng based debuggers. Uses the DAC as an abstraction layer for its operation. The obscure name derives from the original nickname for the CLR team at Microsoft: "Lightning". The team built a debugging tool humorously named "Strike", and a subset of that dubbed "Son of Strike". | | SPCL | `System.Private.CoreLib` - the lowest managed assembly in the libraries stack that contains `System.Object`, `String`, etc. | | SuperPMI | JIT component test framework (super fast JIT testing - it mocks/replays EE in EE-JIT interface) - see [SuperPMI details](https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/superpmi/readme.md). | | SVR | The CLR used to be built as two variants, with one called "mscorsvr.dll", to mean the "server" version. In particular, it contained the server GC implementation, which was intended for multi-threaded apps capable of taking advantage of multiple processors. In the .NET Framework 2 release, the two variants were merged into "mscorwks.dll". The WKS version was the default, however the SVR version remained available. | -| TFM | [Target Framework Moniker](https://docs.microsoft.com/dotnet/standard/frameworks) such as `net6.0` or `netstandard2.0`. | +| TFM | [Target Framework Moniker](https://learn.microsoft.com/dotnet/standard/frameworks) such as `net6.0` or `netstandard2.0`. | | TPA | Trusted Platform Assemblies used to be a special set of assemblies that comprised the platform assemblies, when it was originally designed. As of today, it is simply the set of assemblies known to constitute the application. | | URT | Universal Runtime. Ancient name for what ended up being .NET, is used in the WinError facility name FACILITY_URT. | | UTC | [Universal Tuple Compiler](https://blogs.msdn.microsoft.com/vcblog/2013/06/12/optimizing-c-code-overview/). The Microsoft C++ optimizer back-end that starts by converting the information from the FrontEnd into tuples – a binary stream of instructions. | -| UWP | [Universal Windows Platform (UWP)](https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide) is a platform-homogeneous application architecture available on every device that runs Windows 10. | +| UWP | [Universal Windows Platform (UWP)](https://learn.microsoft.com/windows/uwp/get-started/universal-application-platform-guide) is a platform-homogeneous application architecture available on every device that runs Windows 10. | | VSD | [Virtual Stub Dispatch](../design/coreclr/botr/virtual-stub-dispatch.md). Technique of using stubs for virtual method invocations instead of the traditional virtual method table. | | VM | Virtual machine. | | WKS | The CLR used to be built as two variants, with one called "mscorwks.dll", to mean the "workstation" version. In particular, it contained the client GC implementation, which was intended for single-threaded apps, independent of how many processors were on the machine. In the .NET Framework 2 release, the two variants were merged into "mscorwks.dll". The WKS version was the default, however the SVR version remained available. | @@ -173,18 +173,18 @@ produces binaries in the ReadyToRun file format. ### NGen -[NGen](https://docs.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator) +[NGen](https://learn.microsoft.com/dotnet/framework/tools/ngen-exe-native-image-generator) is AOT technology included in .NET Framework. It usually compiles code at install time on the machine where the code will be executed. ### Full AOT -[Full AOT](https://docs.microsoft.com/en-us/xamarin/ios/internals/architecture) is used +[Full AOT](https://learn.microsoft.com/xamarin/ios/internals/architecture) is used by Mono runtime in environments that prohibit fallback to JIT. ### Hybrid AOT -[Hybrid AOT](https://docs.microsoft.com/en-us/xamarin/mac/internals/aot#hybrid-aot) is used +[Hybrid AOT](https://learn.microsoft.com/xamarin/mac/internals/aot#hybrid-aot) is used by Mono runtime in environments that allow fallback to JIT or need IL interpreter. ### Native AOT @@ -301,8 +301,8 @@ and enabling support for running WPF on .NET Core (Windows Only). [xunit]: https://github.com/xunit [mc.dot.net]: https://mc.dot.net/ [ECMA-355]: https://www.ecma-international.org/publications-and-standards/standards/ecma-335 -[dotnet-tooling]: https://docs.microsoft.com/en-us/dotnet/core/tools/ -[dlr-architecture]: https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview#dlr-architecture +[dotnet-tooling]: https://learn.microsoft.com/dotnet/core/tools/ +[dlr-architecture]: https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview#dlr-architecture [dlr-source]: https://github.com/IronLanguages/dlr [WinForms]: https://github.com/dotnet/winforms [Wpf]: https://github.com/dotnet/wpf diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 41d5a78f3f179..fa109f5726797 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -35,8 +35,8 @@ The acceptance criteria for adding an obsoletion includes: * A bot will automatically apply the `needs-breaking-change-doc-created` label when the `breaking-change` label is detected * Follow up with the breaking change process to communicate and document the breaking change * In the breaking-change issue filed in [dotnet/docs](https://github.com/dotnet/docs), specifically mention that this breaking change is an obsoletion with a `SYSLIB` diagnostic id - * The documentation team will produce a PR that adds the obsoletion to the [SYSLIB warnings](https://docs.microsoft.com/en-us/dotnet/core/compatibility/syslib-obsoletions) page - * That PR will also add a new URL specific to this diagnostic ID; e.g. [SYSLIB0001](https://docs.microsoft.com/en-us/dotnet/core/compatibility/syslib-warnings/syslib0001) + * The documentation team will produce a PR that adds the obsoletion to the [SYSLIB warnings](https://learn.microsoft.com/dotnet/core/compatibility/syslib-obsoletions) page + * That PR will also add a new URL specific to this diagnostic ID; e.g. [SYSLIB0001](https://learn.microsoft.com/dotnet/core/compatibility/syslib-warnings/syslib0001) * Connect with `@gewarren` or `@BillWagner` with any questions * Register the `SYSLIB0###` URL in `aka.ms` * The vanity name will be `dotnet-warnings/syslib0###` @@ -145,7 +145,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL | __`SYSLIB1024`__ | Argument is using the unsupported out parameter modifier | | __`SYSLIB1025`__ | Multiple logging methods cannot use the same event name within a class | | __`SYSLIB1026`__ | C# language version not supported by the logging source generator. | -| __`SYSLIB1027`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | +| __`SYSLIB1027`__ | Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field | | __`SYSLIB1028`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | | __`SYSLIB1029`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | | __`SYSLIB1030`__ | JsonSourceGenerator did not generate serialization metadata for type | diff --git a/docs/project/strong-name-signing.md b/docs/project/strong-name-signing.md index 269bb102c4ad2..e7bde0aab6880 100644 --- a/docs/project/strong-name-signing.md +++ b/docs/project/strong-name-signing.md @@ -1,26 +1,26 @@ Strong Name Signing =================== -All .NET Core assemblies are [strong-named](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/strong-named-assemblies). We do this for two reasons: +All .NET Core assemblies are [strong-named](https://learn.microsoft.com/dotnet/framework/app-domains/strong-named-assemblies). We do this for two reasons: 1. _Compatibility_. We want to maintain type identity with previous versions of our assemblies that have shipped across various versions of our platforms. Removing a strong-name from an assembly is a breaking change, and would break the ability to consume and run libraries built against the previous identities. -2. _Serviceability_. When running on .NET Framework some of .NET Core assemblies ship locally ("app-local") with the application, this is in contrast to other framework assemblies that are placed in the [GAC](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/gac). To be able to service these libraries for critical security updates, we make use of the [app-local servicing](https://blogs.msdn.microsoft.com/dotnet/2014/01/22/net-4-5-1-supports-microsoft-security-updates-for-net-nuget-libraries/) feature which requires that assemblies have strong-names. +2. _Serviceability_. When running on .NET Framework some of .NET Core assemblies ship locally ("app-local") with the application, this is in contrast to other framework assemblies that are placed in the [GAC](https://learn.microsoft.com/dotnet/framework/app-domains/gac). To be able to service these libraries for critical security updates, we make use of the [app-local servicing](https://blogs.msdn.microsoft.com/dotnet/2014/01/22/net-4-5-1-supports-microsoft-security-updates-for-net-nuget-libraries/) feature which requires that assemblies have strong-names. ## FAQ ### 1. Microsoft strong-names their assemblies, should I? For the most part, the majority of applications do not need strong-names. Strong-names are left over from previous eras of .NET where [sandboxing](https://en.wikipedia.org/wiki/Sandbox_(computer_security)) needed to differentiate between code that was trusted, versus code that was untrusted. However in recent years, sandboxing via AppDomains, especially to [isolate ASP.NET web applications](https://support.microsoft.com/en-us/help/2698981/asp-net-partial-trust-does-not-guarantee-application-isolation), is no longer guaranteed and is not recommended. -However, strong-names are still required in applications in some rare situations, most of which are called out on this page: [Strong-Named Assemblies](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/strong-named-assemblies). +However, strong-names are still required in applications in some rare situations, most of which are called out on this page: [Strong-Named Assemblies](https://learn.microsoft.com/dotnet/framework/app-domains/strong-named-assemblies). -Because of the viral nature of strong-naming, it is recommended that publicly published .NET libraries are strong-named. Not strong-naming a .NET library excludes anyone who does need to strong-name their application or library from using it. Read more about .NET libraries and strong-naming in the [.NET Library Guidance](https://docs.microsoft.com/dotnet/standard/library-guidance/strong-naming). +Because of the viral nature of strong-naming, it is recommended that publicly published .NET libraries are strong-named. Not strong-naming a .NET library excludes anyone who does need to strong-name their application or library from using it. Read more about .NET libraries and strong-naming in the [.NET Library Guidance](https://learn.microsoft.com/dotnet/standard/library-guidance/strong-naming). ### 2. I really, _really_ need to strong-name, what kinds of issues will I run into? There are three major problems that developers run into after strong naming their assemblies: -1. _Binding Policy_. When developers talk about strong-names, they are usually conflating it with the strict binding policy of the .NET Framework that kicks in _when_ you strong-name. This binding policy is problematic because it forces, by default, an exact match between reference and version, and requires developers to author complex [binding redirects](https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/bindingredirect-element) when they don't. In recent versions of Visual Studio, however, we've added [Automatic Binding Redirection](https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection) as an attempt to reduce pain of this policy on developers. On top of this, all newer platforms, including _Silverlight_, _WinRT-based platforms_ (Phone and Store), _.NET Native_ and _ASP.NET 5_ this policy has been loosened, allowing later versions of an assembly to satisfy earlier references, thereby completely removing the need to ever write binding redirects on those platforms. +1. _Binding Policy_. When developers talk about strong-names, they are usually conflating it with the strict binding policy of the .NET Framework that kicks in _when_ you strong-name. This binding policy is problematic because it forces, by default, an exact match between reference and version, and requires developers to author complex [binding redirects](https://learn.microsoft.com/dotnet/framework/configure-apps/file-schema/runtime/bindingredirect-element) when they don't. In recent versions of Visual Studio, however, we've added [Automatic Binding Redirection](https://learn.microsoft.com/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection) as an attempt to reduce pain of this policy on developers. On top of this, all newer platforms, including _Silverlight_, _WinRT-based platforms_ (Phone and Store), _.NET Native_ and _ASP.NET 5_ this policy has been loosened, allowing later versions of an assembly to satisfy earlier references, thereby completely removing the need to ever write binding redirects on those platforms. 2. _Virality_. Once you've strong-named an assembly, you can only statically reference other strong-named assemblies. -3. _No drop-in replacement_. This is a problem for open source libraries where the strong-name private key is not checked into the repository. This means that developers are unable to build to their own version of the library and then use it as a drop-in replacement without recompiling _all_ consuming libraries up stack to pick up the new identity. This is extremely problematic for libraries, such as Json.NET, which have large incoming dependencies. Firstly, we would recommend that these open source projects check-in their private key (remember, [strong-names are used for identity, and not for security](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/strong-named-assemblies)). Failing that, however, we've introduced a new concept called [Public Signing](public-signing.md) that enables developers to build drop-in replacements without needing access to the strong-name private key. This is the mechanism that .NET Core libraries use by default. +3. _No drop-in replacement_. This is a problem for open source libraries where the strong-name private key is not checked into the repository. This means that developers are unable to build to their own version of the library and then use it as a drop-in replacement without recompiling _all_ consuming libraries up stack to pick up the new identity. This is extremely problematic for libraries, such as Json.NET, which have large incoming dependencies. Firstly, we would recommend that these open source projects check-in their private key (remember, [strong-names are used for identity, and not for security](https://learn.microsoft.com/dotnet/framework/app-domains/strong-named-assemblies)). Failing that, however, we've introduced a new concept called [Public Signing](public-signing.md) that enables developers to build drop-in replacements without needing access to the strong-name private key. This is the mechanism that .NET Core libraries use by default. diff --git a/docs/tools/illink/README.md b/docs/tools/illink/README.md index 7aed29d0ea76d..c837328dbdd73 100644 --- a/docs/tools/illink/README.md +++ b/docs/tools/illink/README.md @@ -3,7 +3,7 @@ This folder includes several documents that explain both high-level and low-level concepts about the IL trimmer and related tools. ## Official Docs -View the official trimming docs at [docs.microsoft.com](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained) +View the official trimming docs at [learn.microsoft.com](https://learn.microsoft.com/dotnet/core/deploying/trimming/trim-self-contained) ## Usage Docs - [MSBuild Integration](illink-tasks.md) diff --git a/docs/tools/illink/custom-steps.md b/docs/tools/illink/custom-steps.md index d7ce8576c9068..9adf1687cb1ac 100644 --- a/docs/tools/illink/custom-steps.md +++ b/docs/tools/illink/custom-steps.md @@ -3,7 +3,7 @@ The IL trimmer behaviour can be altered not only using existing options but also by adding custom steps to the processing pipeline. This advanced technique is not necessary for most scenarious but can be used by additional framework or SDKs which need very -custom behaviour or have other special needs for the processing. +custom behaviour or have other special requirements for the processing. ## Building diff --git a/docs/tools/illink/illink-options.md b/docs/tools/illink/illink-options.md index 76d8e26837671..1ed9928c08fc7 100644 --- a/docs/tools/illink/illink-options.md +++ b/docs/tools/illink/illink-options.md @@ -110,7 +110,7 @@ Each feature can be controlled independently using `--feature NAME value` option The list of available feature names is framework-dependent and can vary between different framework versions. -The list of controllable features for .NET Core is available at https://docs.microsoft.com/en-us/dotnet/core/run-time-config/. +The list of controllable features for .NET Core is available at https://learn.microsoft.com/dotnet/core/run-time-config/. ### Using custom substitutions diff --git a/docs/tools/illink/serialization.md b/docs/tools/illink/serialization.md index d93f50bd8db3e..648b9a226cb9d 100644 --- a/docs/tools/illink/serialization.md +++ b/docs/tools/illink/serialization.md @@ -1,6 +1,6 @@ # Serialization -Trimming tools cannot analyze the patterns typically used by reflection-based serializers. Such serializers should be annotated with `RequiresUnreferencedCodeAttribute`, and using them in a trimmed app will likely not work (or will work unpredictably). Trimming tools will produce static analysis [warnings](https://docs.microsoft.com/dotnet/core/deploying/trimming-options#analysis-warnings) for these patterns. +Trimming tools cannot analyze the patterns typically used by reflection-based serializers. Such serializers should be annotated with `RequiresUnreferencedCodeAttribute`, and using them in a trimmed app will likely not work (or will work unpredictably). Trimming tools will produce static analysis [warnings](https://learn.microsoft.com/dotnet/core/deploying/trimming-options#analysis-warnings) for these patterns. If possible, avoid using reflection-based serializers with trimming, and prefer solutions based on source generators where the serialized types and all required members are statically referenced. @@ -129,12 +129,12 @@ Most features of reflection-based serializers will not work even with these heur - Serializing/deserializing types which are not attributed and don't have attributed members - Passing `typeof(MyType)` (directly or indirectly) into serializer constructors or methods - "Known type" mechanisms, such as: - - [`KnownTypeAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.serialization.knowntypeattribute?view=net-5.0) - - [`DataContractSerializer.KnownTypes`](https://docs.microsoft.com/dotnet/api/system.runtime.serialization.datacontractserializer.knowntypes?view=net-5.0) - - `extraTypes` argument of the [`XmlSerializer ctor`](https://docs.microsoft.com/dotnet/api/system.xml.serialization.xmlserializer.-ctor?view=net-5.0#System_Xml_Serialization_XmlSerializer__ctor_System_Type_System_Type___) + - [`KnownTypeAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.serialization.knowntypeattribute?view=net-5.0) + - [`DataContractSerializer.KnownTypes`](https://learn.microsoft.com/dotnet/api/system.runtime.serialization.datacontractserializer.knowntypes?view=net-5.0) + - `extraTypes` argument of the [`XmlSerializer ctor`](https://learn.microsoft.com/dotnet/api/system.xml.serialization.xmlserializer.-ctor?view=net-5.0#System_Xml_Serialization_XmlSerializer__ctor_System_Type_System_Type___) - Serializing types which implement special interfaces - - [`ISerializable`](https://docs.microsoft.com/dotnet/api/system.runtime.serialization.iserializable?view=net-5.0) - - [`IXmlSerializable`](https://docs.microsoft.com/dotnet/api/system.xml.serialization.ixmlserializable?view=net-5.0) + - [`ISerializable`](https://learn.microsoft.com/dotnet/api/system.runtime.serialization.iserializable?view=net-5.0) + - [`IXmlSerializable`](https://learn.microsoft.com/dotnet/api/system.xml.serialization.ixmlserializable?view=net-5.0) - Serializer-specific handling of collection types - - Types which implement [`ICollection`](https://docs.microsoft.com/dotnet/standard/serialization/examples-of-xml-serialization#serializing-a-class-that-implements-the-icollection-interface) - - Deserializing [`collection interfaces`](https://docs.microsoft.com/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts#using-collection-interface-types-and-read-only-collections) into serializer-specific default types + - Types which implement [`ICollection`](https://learn.microsoft.com/dotnet/standard/serialization/examples-of-xml-serialization#serializing-a-class-that-implements-the-icollection-interface) + - Deserializing [`collection interfaces`](https://learn.microsoft.com/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts#using-collection-interface-types-and-read-only-collections) into serializer-specific default types diff --git a/docs/workflow/building/coreclr/cross-building.md b/docs/workflow/building/coreclr/cross-building.md index 7bbe28abb6410..d053569cfbcb3 100644 --- a/docs/workflow/building/coreclr/cross-building.md +++ b/docs/workflow/building/coreclr/cross-building.md @@ -142,7 +142,7 @@ If you're building the cross-components in powershell, you'll need to wrap `"-DC ## Cross-Building using Docker -When it comes to building, Docker offers the most flexibility when it comes to targeting different Linux platforms and other similar Unix-based ones, like FreeBSD. This is thanks to the multiple existing Docker images already configured for doing such cross-platform building, and Docker's ease of use of running out of the box on Windows machines with [WSL](https://docs.microsoft.com/windows/wsl/about) enabled, installed, and up and running, as well as Linux machines. +When it comes to building, Docker offers the most flexibility when it comes to targeting different Linux platforms and other similar Unix-based ones, like FreeBSD. This is thanks to the multiple existing Docker images already configured for doing such cross-platform building, and Docker's ease of use of running out of the box on Windows machines with [WSL](https://learn.microsoft.com/windows/wsl/about) enabled, installed, and up and running, as well as Linux machines. ### Cross-Compiling for ARM32 and ARM64 with Docker diff --git a/docs/workflow/building/coreclr/linux-instructions.md b/docs/workflow/building/coreclr/linux-instructions.md index 3acec5e95ad9a..f19bcfbd1e7e2 100644 --- a/docs/workflow/building/coreclr/linux-instructions.md +++ b/docs/workflow/building/coreclr/linux-instructions.md @@ -19,7 +19,7 @@ As mentioned in the [Linux requirements doc](/docs/workflow/requirements/linux-r Building using Docker will require that you choose the correct image for your environment. -Note that the OS is strictly speaking not important. For example if you are on Ubuntu 20.04 and build using the Ubuntu 18.04 x64 image there should be no issues. You can even use Linux images on a Windows OS if you have [WSL](https://docs.microsoft.com/windows/wsl/about) enabled. However, note that you can't run multiple OS's on the same _Docker Daemon_, as it takes resources from the underlying kernel as needed. In other words, you can run either Linux on WSL, or Windows containers. You have to switch between them if you need both, and restart Docker. +Note that the OS is strictly speaking not important. For example if you are on Ubuntu 20.04 and build using the Ubuntu 18.04 x64 image there should be no issues. You can even use Linux images on a Windows OS if you have [WSL](https://learn.microsoft.com/windows/wsl/about) enabled. However, note that you can't run multiple OS's on the same _Docker Daemon_, as it takes resources from the underlying kernel as needed. In other words, you can run either Linux on WSL, or Windows containers. You have to switch between them if you need both, and restart Docker. The target architecture is more important, as building arm32 using the x64 image will not work. There will be missing _rootfs_ components required by the build. See [Docker Images](#docker-images) below, for more information on choosing an image to build with. diff --git a/docs/workflow/ci/coreclr-ci-health.md b/docs/workflow/ci/coreclr-ci-health.md index 79be9ac1bfebf..b26bfcab8db21 100644 --- a/docs/workflow/ci/coreclr-ci-health.md +++ b/docs/workflow/ci/coreclr-ci-health.md @@ -13,7 +13,7 @@ Note that this document focuses on coreclr testing in `dotnet/runtime`. #### Terminology -In order to follow some of the terminology used, there is an expected familiarity of Azure DevOps required. For an in depth guide with Azure DevOps pipeline definitions, please see: https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema. +In order to follow some of the terminology used, there is an expected familiarity of Azure DevOps required. For an in depth guide with Azure DevOps pipeline definitions, please see: https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema. The most common terminology and most important are the different containers work happens in. diff --git a/docs/workflow/debugging/coreclr/debugging-runtime.md b/docs/workflow/debugging/coreclr/debugging-runtime.md index 6edce3c7646e0..e9e1881063d63 100644 --- a/docs/workflow/debugging/coreclr/debugging-runtime.md +++ b/docs/workflow/debugging/coreclr/debugging-runtime.md @@ -113,7 +113,7 @@ Visual Studio Code instructions coming soon! Under normal circumstances, SOS usually comes shipped with Windbg, so no additional installation is required. However, if this is not the case for you, you want to use another version, or any other circumstance that requires you to install it separately/additionally, here are two links with useful information on how to get it set up: -* The official [Microsoft docs on SOS](https://docs.microsoft.com/dotnet/core/diagnostics/dotnet-sos). +* The official [Microsoft docs on SOS](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). * The instructions at the [diagnostics repo](https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-windows-instructions.md). For more information on SOS commands click [here](https://github.com/dotnet/diagnostics/blob/master/documentation/sos-debugging-extension-windows.md). @@ -140,7 +140,7 @@ If for some reason `System.Private.CoreLib.dll` is missing, you can rebuild it w For Linux and macOS, you have to install SOS by yourself, as opposed to Windows' Windbg. The instructions are very similar however, and you can find them on these two links: -* The official [Microsoft docs on SOS](https://docs.microsoft.com/dotnet/core/diagnostics/dotnet-sos). +* The official [Microsoft docs on SOS](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos). * The instructions at the [diagnostics repo](https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-instructions.md). It might also be the case that you would need the latest changes in SOS, or you're working with a not-officially-supported scenario that actually works. The most common occurrence of this scenario is when using macOS Arm64. In this case, you have to build SOS from the diagnostics repo (linked above). Once you have it done, then simply load it to your `lldb`. More details in the following section. @@ -227,11 +227,11 @@ Starting with Visual Studio 2022 version 17.5, Visual Studio will validate that > Unable to attach to CoreCLR. Signature validation failed for a .NET Runtime Debugger library because the file is unsigned. > -> This error is expected if you are working with non-official releases of .NET (example: daily builds from https://github.com/dotnet/installer). See https://aka.ms/vs/unsigned-dotnet-debugger-lib for more information. +> This error is expected if you are working with non-official releases of .NET (example: daily builds from https://github.com/dotnet/sdk). See https://aka.ms/vs/unsigned-dotnet-debugger-lib for more information. If the target process is using a .NET Runtime that is either from a daily build, or one that you built on your own computer, this error will show up. **NOTE**: This error should never happen for official builds of the .NET Runtime from Microsoft. So don’t disable the validation if you expect to be using a .NET Runtime supported by Microsoft. There are three ways to configure Visual Studio to disable signature validation: -1. The [`DOTNET_ROOT` environment variable](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_root-dotnet_rootx86): if Visual Studio is started from a command prompt where `DOTNET_ROOT` is set, it will ignore unsigned .NET runtime debugger libraries which are under the `DOTNET_ROOT` directory. +1. The [`DOTNET_ROOT` environment variable](https://learn.microsoft.com/dotnet/core/tools/dotnet-environment-variables#dotnet_root-dotnet_rootx86): if Visual Studio is started from a command prompt where `DOTNET_ROOT` is set, it will ignore unsigned .NET runtime debugger libraries which are under the `DOTNET_ROOT` directory. 2. The `VSDebugger_ValidateDotnetDebugLibSignatures` environment variable: If you want to temporarily disable signature validation, run `set VSDebugger_ValidateDotnetDebugLibSignatures=0` in a command prompt, and start Visual Studio (devenv.exe) from this command prompt. 3. Set the `ValidateDotnetDebugLibSignatures` registry key: To disable signature validation on a more permanent basis, you can set the VS registry key to turn it off. To do so, open a Developer Command Prompt, and run `Common7\IDE\VsRegEdit.exe set local HKCU Debugger\EngineSwitches ValidateDotnetDebugLibSignatures dword 0` diff --git a/docs/workflow/debugging/libraries/unix-instructions.md b/docs/workflow/debugging/libraries/unix-instructions.md index 7e6b7700839af..5063b1f414719 100644 --- a/docs/workflow/debugging/libraries/unix-instructions.md +++ b/docs/workflow/debugging/libraries/unix-instructions.md @@ -5,7 +5,7 @@ CoreFX can be debugged on unix using both lldb and visual studio code ## Using lldb and SOS -- Install SOS and lldb. See https://github.com/dotnet/diagnostics/blob/main/documentation/sos.md and https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-sos for setup instructions. +- Install SOS and lldb. See https://github.com/dotnet/diagnostics/blob/main/documentation/sos.md and https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos for setup instructions. - Run the test using msbuild at least once with `/t:Test`. ## Debugging core dumps with lldb diff --git a/docs/workflow/requirements/windows-requirements.md b/docs/workflow/requirements/windows-requirements.md index 86c1fca3a7f3d..7eb85bec22fcc 100644 --- a/docs/workflow/requirements/windows-requirements.md +++ b/docs/workflow/requirements/windows-requirements.md @@ -20,7 +20,7 @@ Here are the components you will need to install and setup to work with the repo ### Enable Long Paths -The runtime repository requires long paths to be enabled. Follow [the instructions provided here](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enable-long-paths-in-windows-10-version-1607-and-later) to enable that feature. +The runtime repository requires long paths to be enabled. Follow [the instructions provided here](https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enable-long-paths-in-windows-10-version-1607-and-later) to enable that feature. If using Git for Windows you might need to also configure long paths there. Using an administrator terminal simply type: @@ -42,7 +42,7 @@ Note that Visual Studio and the development tools described below are required, * To build the tests, you will need some additional components: * **C++/CLI support for v143 build tools (Latest)**. -A `.vsconfig` file is included in the root of the _dotnet/runtime_ repository that includes all components needed to build the _dotnet/runtime_ repository. You can [import `.vsconfig` in your Visual Studio installer](https://docs.microsoft.com/visualstudio/install/import-export-installation-configurations?view=vs-2022#import-a-configuration) to install all necessary components. +A `.vsconfig` file is included in the root of the _dotnet/runtime_ repository that includes all components needed to build the _dotnet/runtime_ repository. You can [import `.vsconfig` in your Visual Studio installer](https://learn.microsoft.com/visualstudio/install/import-export-installation-configurations?view=vs-2022#import-a-configuration) to install all necessary components. ### Build Tools @@ -89,7 +89,7 @@ The _dotnet/runtime_ repository requires at least Git 2.22.0. While not strictly needed to build or test this repository, having the .NET SDK installed lets you browse solution files in this repository with Visual Studio and use the `dotnet.exe` command to run .NET applications in the 'normal' way. -We use this in the [build testing with the installed SDK](/docs/workflow/testing/using-your-build-with-installed-sdk.md), and [build testing with dev shipping packages](/docs/workflow/testing/using-dev-shipping-packages.md) instructions. The minimum required version of the SDK is specified in the [global.json file](https://github.com/dotnet/runtime/blob/main/global.json#L3). You can find the installers and binaries for latest development builds of .NET SDK in the [installer repo](https://github.com/dotnet/installer#installers-and-binaries). +We use this in the [build testing with the installed SDK](/docs/workflow/testing/using-your-build-with-installed-sdk.md), and [build testing with dev shipping packages](/docs/workflow/testing/using-dev-shipping-packages.md) instructions. The minimum required version of the SDK is specified in the [global.json file](https://github.com/dotnet/runtime/blob/main/global.json#L3). You can find the installers and binaries for latest development builds of .NET SDK in the [sdk repo](https://github.com/dotnet/sdk#installing-the-sdk). Alternatively, to avoid modifying your machine state, you can use the repository's locally acquired SDK by passing in the solution to load via the `-vs` switch. For example: diff --git a/docs/workflow/testing/host/testing.md b/docs/workflow/testing/host/testing.md index a217d1dd0ab98..65f2f92b0d8c3 100644 --- a/docs/workflow/testing/host/testing.md +++ b/docs/workflow/testing/host/testing.md @@ -64,12 +64,12 @@ By default, the above command will also build the tests before running them. To If all tests have not been previously run, make sure the [test context](#test-context) is set up for the test library. -Tests from a specific test project can be run using [`dotnet test`](https://docs.microsoft.com/dotnet/core/tools/dotnet-test) targeting the built test binary. For example: +Tests from a specific test project can be run using [`dotnet test`](https://learn.microsoft.com/dotnet/core/tools/dotnet-test) targeting the built test binary. For example: ``` dotnet test artifacts/bin/HostActivation.Tests/Debug/net6.0/HostActivation.Tests.dll --filter category!=failing ``` -To filter to specific tests within the test library, use the [filter options](https://docs.microsoft.com/dotnet/core/tools/dotnet-test#filter-option-details) available for `dotnet test`. For example: +To filter to specific tests within the test library, use the [filter options](https://learn.microsoft.com/dotnet/core/tools/dotnet-test#filter-option-details) available for `dotnet test`. For example: ``` dotnet test artifacts/bin/HostActivation.Tests/Debug/net6.0/HostActivation.Tests.dll --filter "DependencyResolution&category!=failing" ``` diff --git a/docs/workflow/testing/host/using-apphost.md b/docs/workflow/testing/host/using-apphost.md index 764dc6ad68e58..f3a3f5309df10 100644 --- a/docs/workflow/testing/host/using-apphost.md +++ b/docs/workflow/testing/host/using-apphost.md @@ -25,6 +25,6 @@ Alternatives to this method include copying the desired apphost to the appropria # Pointing at a local .NET root -For a [framework-dependent application](https://docs.microsoft.com/dotnet/core/deploying/#publish-framework-dependent), you can set the `DOTNET_ROOT` environment variable to point at a local .NET layout. +For a [framework-dependent application](https://learn.microsoft.com/dotnet/core/deploying/#publish-framework-dependent), you can set the `DOTNET_ROOT` environment variable to point at a local .NET layout. The [libraries tests](../libraries/testing.md) construct and use such a layout based on your local runtime, host, and libraries build as part of the `libs.pretest` subset. To use that layout, set `DOTNET_ROOT=/artifacts/bin/testhost/net8.0---` and then run the .NET application. diff --git a/docs/workflow/testing/testing-managed-tools.md b/docs/workflow/testing/testing-managed-tools.md index 6e2fddbdbd773..589e689a1803d 100644 --- a/docs/workflow/testing/testing-managed-tools.md +++ b/docs/workflow/testing/testing-managed-tools.md @@ -41,5 +41,5 @@ The `dotnet-test` xunit filter mechanisms work to run a single test or a subset ``` The above command runs all tests whose fully-qualified name contains the substring `MyTest`. See -[dotnet test - Run selective unit tests](https://learn.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=mstest#syntax) +[dotnet test - Run selective unit tests](https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests?pivots=mstest#syntax) for the full syntax. diff --git a/docs/workflow/testing/using-dev-shipping-packages.md b/docs/workflow/testing/using-dev-shipping-packages.md index 6510b43d22474..1d801886e8a9f 100644 --- a/docs/workflow/testing/using-dev-shipping-packages.md +++ b/docs/workflow/testing/using-dev-shipping-packages.md @@ -40,7 +40,7 @@ This will place several installers, Nuget packages, compressed archives, and oth ### Acquire the latest development .NET SDK -The [installer repo](https://github.com/dotnet/installer) has downloads to all nightly builds for all the currently supported platforms. Find the one that matches your machine and download it. +The [sdk repo](https://github.com/dotnet/sdk#installing-the-sdk) has downloads to all nightly builds for all the currently supported platforms. Find the one that matches your machine and download it. To setup the nightly SDK, you can either install it to your machine or use a portable build. If you downloaded the _installer_, then just follow the usual installation instructions, and you're done. @@ -80,7 +80,7 @@ This config file will require a handful of modifications to work as we need it t - + diff --git a/docs/workflow/testing/using-your-build-with-installed-sdk.md b/docs/workflow/testing/using-your-build-with-installed-sdk.md index a2716ea9e7aad..200f035250fcd 100644 --- a/docs/workflow/testing/using-your-build-with-installed-sdk.md +++ b/docs/workflow/testing/using-your-build-with-installed-sdk.md @@ -30,7 +30,7 @@ This guide focuses on the first of the bullet points described above. For the ot ### Acquire the latest development .NET SDK -The [installer repo](https://github.com/dotnet/installer) has downloads to all nightly builds for all the currently supported platforms. Find the one that matches your machine and download it. +The [sdk repo](https://github.com/dotnet/sdk#installing-the-sdk) has downloads to all nightly builds for all the currently supported platforms. Find the one that matches your machine and download it. To setup the nightly SDK, you can either install it to your machine or use a portable build. If you downloaded the _installer_, then just follow the usual installation instructions, and you're done. diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9fde4018aacb7..4b3aaaab38bba 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,9 +12,9 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - b0e8c5f3800bbaa46221ff26b3748f0a2b486f8c + fae2c9534679912d43304de91e622f63e7110919 https://github.com/dotnet/llvm-project @@ -68,111 +68,111 @@ 7a4a59f9f66baf6711a6ce2de01d3b2c62ed72d8 - + https://github.com/dotnet/emsdk - b0e8c5f3800bbaa46221ff26b3748f0a2b486f8c + fae2c9534679912d43304de91e622f63e7110919 - + https://github.com/dotnet/emsdk - b0e8c5f3800bbaa46221ff26b3748f0a2b486f8c + fae2c9534679912d43304de91e622f63e7110919 - + https://github.com/dotnet/source-build-reference-packages - 0df72d85186994facaefcb4eb832b8c8a8e5ae3d + 87ebd07371adfe1d8140889c33da692eb2134f1a - + https://github.com/dotnet/source-build-externals - 59204e5b14e6e197b3c942f992f6e3ec9196e50b + 7db00527ef8fbbe61f67e9295beebddf187efff8 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 https://github.com/dotnet/runtime-assets @@ -332,9 +332,9 @@ https://github.com/dotnet/xharness 8478d1a9a531e6b1cd26fb8c86400e2efbb8f2f3 - + https://github.com/dotnet/arcade - 2001d73c8ff942331a73300ba61fa6164805b231 + e6b3f32f9855dccbe2447471c8f729b66f17d242 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -360,17 +360,17 @@ https://github.com/dotnet/runtime-assets c185f58a7f75bd824a3cd820634cddf27e791d91 - + https://github.com/dotnet/roslyn - 2e1435d1aadd8ddb90a171e207e3cb2ae67253f2 + 37b70f27ecf6578ad0b74d04406036b03ab90c8f - + https://github.com/dotnet/roslyn - 2e1435d1aadd8ddb90a171e207e3cb2ae67253f2 + 37b70f27ecf6578ad0b74d04406036b03ab90c8f - + https://github.com/dotnet/roslyn - 2e1435d1aadd8ddb90a171e207e3cb2ae67253f2 + 37b70f27ecf6578ad0b74d04406036b03ab90c8f https://github.com/dotnet/roslyn-analyzers @@ -381,9 +381,9 @@ 8dccccec1ce3bd2fb532ec77d7e092ab9d684db7 - + https://github.com/dotnet/roslyn - 2e1435d1aadd8ddb90a171e207e3cb2ae67253f2 + 37b70f27ecf6578ad0b74d04406036b03ab90c8f diff --git a/eng/Versions.props b/eng/Versions.props index d76d352b6b508..2d215aba85668 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -11,7 +11,7 @@ 7.0.19 6.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet8)').Build),25)) preview - 5 + 6 false release @@ -42,9 +42,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.11.0-2.24274.2 - 4.11.0-2.24274.2 - 4.11.0-2.24274.2 + 4.11.0-3.24301.1 + 4.11.0-3.24301.1 + 4.11.0-3.24301.1 9.0.100-preview.5.24272.19 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 2.6.7-beta.24272.5 - 9.0.0-beta.24272.5 - 2.6.7-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 - 9.0.0-beta.24272.5 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 2.6.7-beta.24281.1 + 9.0.0-beta.24281.1 + 2.6.7-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 + 9.0.0-beta.24281.1 1.4.0 @@ -161,7 +161,7 @@ 1.0.0-prerelease.24223.3 2.0.0 - 17.8.0-beta1.23475.2 + 17.10.0-beta1.24272.1 2.0.0-beta4.23407.1 3.1.7 2.1.0 @@ -235,9 +235,9 @@ Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-9_0_100_Transport --> - 9.0.0-preview.6.24272.2 + 9.0.0-preview.6.24277.2 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-preview.6.24272.2 + 9.0.0-preview.6.24277.2 1.1.87-gba258badda 1.0.0-v3.14.0.5722 diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index efa2fd72bfaa2..c07f6a52601bd 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -1,31 +1,32 @@ -# This file is a temporary workaround for internal builds to be able to restore from private AzDO feeds. -# This file should be removed as part of this issue: https://github.com/dotnet/arcade/issues/4080 +# This script adds internal feeds required to build commits that depend on internal package sources. For instance, +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables +# disabled internal Maestro (darc-int*) feeds. +# +# Optionally, this script also adds a credential entry for each of the internal feeds if supplied. This credential +# is added via the standard environment variable VSS_NUGET_EXTERNAL_FEED_ENDPOINTS. See +# https://github.com/microsoft/artifacts-credprovider/tree/v1.1.1?tab=readme-ov-file#environment-variables for more details # -# What the script does is iterate over all package sources in the pointed NuGet.config and add a credential entry -# under for each Maestro managed private feed. Two additional credential -# entries are also added for the two private static internal feeds: dotnet3-internal and dotnet3-internal-transport. -# -# This script needs to be called in every job that will restore packages and which the base repo has -# private AzDO feeds in the NuGet.config. -# -# See example YAML call for this script below. Note the use of the variable `$(dn-bot-dnceng-artifact-feeds-rw)` -# from the AzureDevOps-Artifact-Feeds-Pats variable group. -# -# Any disabledPackageSources entries which start with "darc-int" will be re-enabled as part of this script executing +# See example call for this script below. # # - task: PowerShell@2 -# displayName: Setup Private Feeds Credentials +# displayName: Setup Internal Feeds # condition: eq(variables['Agent.OS'], 'Windows_NT') # inputs: # filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 -# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token -# env: -# Token: $(dn-bot-dnceng-artifact-feeds-rw) +# arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config +# - task: NuGetAuthenticate@1 +# +# Note that the NuGetAuthenticate task should be called after SetupNugetSources. +# This ensures that: +# - Appropriate creds are set for the added internal feeds (if not supplied to the scrupt) +# - The credential provider is installed +# +# This logic is also abstracted into enable-internal-sources.yml. [CmdletBinding()] param ( [Parameter(Mandatory = $true)][string]$ConfigFile, - [Parameter(Mandatory = $true)][string]$Password + [string]$Password ) $ErrorActionPreference = "Stop" @@ -34,12 +35,23 @@ Set-StrictMode -Version 2.0 . $PSScriptRoot\tools.ps1 +$feedEndpoints = $null + +# If a credential is provided, ensure that we don't overwrite the current set of +# credentials that may have been provided by a previous call to the credential provider. +if ($Password -and $null -ne $env:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS) { + $feedEndpoints = $env:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS | ConvertFrom-Json +} elseif ($Password) { + $feedEndpoints = @{ endpointCredentials = @() } +} + # Add source entry to PackageSources -function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { +function AddPackageSource($sources, $SourceName, $SourceEndPoint, $pwd) { $packageSource = $sources.SelectSingleNode("add[@key='$SourceName']") - if ($packageSource -eq $null) + if ($null -eq $packageSource) { + Write-Host "`tAdding package source" $SourceName $packageSource = $doc.CreateElement("add") $packageSource.SetAttribute("key", $SourceName) $packageSource.SetAttribute("value", $SourceEndPoint) @@ -48,58 +60,34 @@ function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Usern else { Write-Host "Package source $SourceName already present." } - AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd -} - -# Add a credential node for the specified source -function AddCredential($creds, $source, $username, $pwd) { - # Looks for credential configuration for the given SourceName. Create it if none is found. - $sourceElement = $creds.SelectSingleNode($Source) - if ($sourceElement -eq $null) - { - $sourceElement = $doc.CreateElement($Source) - $creds.AppendChild($sourceElement) | Out-Null - } - # Add the node to the credential if none is found. - $usernameElement = $sourceElement.SelectSingleNode("add[@key='Username']") - if ($usernameElement -eq $null) - { - $usernameElement = $doc.CreateElement("add") - $usernameElement.SetAttribute("key", "Username") - $sourceElement.AppendChild($usernameElement) | Out-Null + if ($pwd) { + $feedEndpoints.endpointCredentials = AddCredential -endpointCredentials $feedEndpoints.endpointCredentials -source $SourceEndPoint -pwd $pwd } - $usernameElement.SetAttribute("value", $Username) +} - # Add the to the credential if none is found. - # Add it as a clear text because there is no support for encrypted ones in non-windows .Net SDKs. - # -> https://github.com/NuGet/Home/issues/5526 - $passwordElement = $sourceElement.SelectSingleNode("add[@key='ClearTextPassword']") - if ($passwordElement -eq $null) - { - $passwordElement = $doc.CreateElement("add") - $passwordElement.SetAttribute("key", "ClearTextPassword") - $sourceElement.AppendChild($passwordElement) | Out-Null +# Add a new feed endpoint credential +function AddCredential([array]$endpointCredentials, $source, $pwd) { + $endpointCredentials += @{ + endpoint = $source; + password = $pwd } - - $passwordElement.SetAttribute("value", $pwd) + return $endpointCredentials } -function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $pwd) { - $maestroPrivateSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]") +function InsertMaestroInternalFeedCredentials($Sources, $pwd) { + $maestroInternalSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]") - Write-Host "Inserting credentials for $($maestroPrivateSources.Count) Maestro's private feeds." - - ForEach ($PackageSource in $maestroPrivateSources) { - Write-Host "`tInserting credential for Maestro's feed:" $PackageSource.Key - AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -pwd $pwd + ForEach ($PackageSource in $maestroInternalSources) { + Write-Host "`tAdding credential for Maestro's feed:" $PackageSource.Key + $feedEndpoints.endpointCredentials = AddCredential -endpointCredentials $feedEndpoints.endpointCredentials -source $PackageSource.value -pwd $pwd } } -function EnablePrivatePackageSources($DisabledPackageSources) { - $maestroPrivateSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") - ForEach ($DisabledPackageSource in $maestroPrivateSources) { - Write-Host "`tEnsuring private source '$($DisabledPackageSource.key)' is enabled by deleting it from disabledPackageSource" +function EnableInternalPackageSources($DisabledPackageSources) { + $maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") + ForEach ($DisabledPackageSource in $maestroInternalSources) { + Write-Host "`tEnsuring internal source '$($DisabledPackageSource.key)' is enabled by deleting it from disabledPackageSource" # Due to https://github.com/NuGet/Home/issues/10291, we must actually remove the disabled entries $DisabledPackageSources.RemoveChild($DisabledPackageSource) } @@ -110,11 +98,6 @@ if (!(Test-Path $ConfigFile -PathType Leaf)) { ExitWithExitCode 1 } -if (!$Password) { - Write-PipelineTelemetryError -Category 'Build' -Message 'Eng/common/SetupNugetSources.ps1 returned a non-zero exit code. Please supply a valid PAT' - ExitWithExitCode 1 -} - # Load NuGet.config $doc = New-Object System.Xml.XmlDocument $filename = (Get-Item $ConfigFile).FullName @@ -122,35 +105,27 @@ $doc.Load($filename) # Get reference to or create one if none exist already $sources = $doc.DocumentElement.SelectSingleNode("packageSources") -if ($sources -eq $null) { +if ($null -eq $sources) { $sources = $doc.CreateElement("packageSources") $doc.DocumentElement.AppendChild($sources) | Out-Null } -# Looks for a node. Create it if none is found. -$creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials") -if ($creds -eq $null) { - $creds = $doc.CreateElement("packageSourceCredentials") - $doc.DocumentElement.AppendChild($creds) | Out-Null -} - # Check for disabledPackageSources; we'll enable any darc-int ones we find there $disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources") -if ($disabledSources -ne $null) { +if ($null -ne $disabledSources) { Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node" - EnablePrivatePackageSources -DisabledPackageSources $disabledSources + EnableInternalPackageSources -DisabledPackageSources $disabledSources } -$userName = "dn-bot" - -# Insert credential nodes for Maestro's private feeds -InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -pwd $Password +if ($Password) { + InsertMaestroInternalFeedCredentials -Sources $sources -pwd $Password +} # 3.1 uses a different feed url format so it's handled differently here $dotnet31Source = $sources.SelectSingleNode("add[@key='dotnet3.1']") -if ($dotnet31Source -ne $null) { - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password +if ($null -ne $dotnet31Source) { + AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v3/index.json" -pwd $Password + AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v3/index.json" -pwd $Password } $dotnetVersions = @('5','6','7','8') @@ -158,10 +133,18 @@ $dotnetVersions = @('5','6','7','8') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; $dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']") - if ($dotnetSource -ne $null) { - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password + if ($dotnetSource) { + AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedprefix-internal/nuget/v3/index.json" -pwd $Password + AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v3/index.json" -pwd $Password } } -$doc.Save($filename) \ No newline at end of file +$doc.Save($filename) + +# If any credentials were added or altered, update the VSS_NUGET_EXTERNAL_FEED_ENDPOINTS environment variable +if ($null -ne $feedEndpoints) { + # ci is set to true so vso logging commands will be used. + $ci = $true + Write-PipelineSetVariable -Name 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' -Value $($feedEndpoints | ConvertTo-Json) -IsMultiJobVariable $false + Write-PipelineSetVariable -Name 'NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED' -Value "False" -IsMultiJobVariable $false +} \ No newline at end of file diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index d387c7eac95e5..16c1e29ea3b79 100644 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -1,28 +1,27 @@ #!/usr/bin/env bash -# This file is a temporary workaround for internal builds to be able to restore from private AzDO feeds. -# This file should be removed as part of this issue: https://github.com/dotnet/arcade/issues/4080 +# This script adds internal feeds required to build commits that depend on intenral package sources. For instance, +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables +# disabled internal Maestro (darc-int*) feeds. +# +# Optionally, this script also adds a credential entry for each of the internal feeds if supplied. # -# What the script does is iterate over all package sources in the pointed NuGet.config and add a credential entry -# under for each Maestro's managed private feed. Two additional credential -# entries are also added for the two private static internal feeds: dotnet3-internal and dotnet3-internal-transport. -# -# This script needs to be called in every job that will restore packages and which the base repo has -# private AzDO feeds in the NuGet.config. -# -# See example YAML call for this script below. Note the use of the variable `$(dn-bot-dnceng-artifact-feeds-rw)` -# from the AzureDevOps-Artifact-Feeds-Pats variable group. -# -# Any disabledPackageSources entries which start with "darc-int" will be re-enabled as part of this script executing. +# See example call for this script below. # # - task: Bash@3 -# displayName: Setup Private Feeds Credentials +# displayName: Setup Internal Feeds # inputs: # filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh -# arguments: $(Build.SourcesDirectory)/NuGet.config $Token +# arguments: $(Build.SourcesDirectory)/NuGet.config # condition: ne(variables['Agent.OS'], 'Windows_NT') -# env: -# Token: $(dn-bot-dnceng-artifact-feeds-rw) +# - task: NuGetAuthenticate@1 +# +# Note that the NuGetAuthenticate task should be called after SetupNugetSources. +# This ensures that: +# - Appropriate creds are set for the added internal feeds (if not supplied to the scrupt) +# - The credential provider is installed. +# +# This logic is also abstracted into enable-internal-sources.yml. ConfigFile=$1 CredToken=$2 @@ -48,11 +47,6 @@ if [ ! -f "$ConfigFile" ]; then ExitWithExitCode 1 fi -if [ -z "$CredToken" ]; then - Write-PipelineTelemetryError -category 'Build' "Error: Eng/common/SetupNugetSources.sh returned a non-zero exit code. Please supply a valid PAT" - ExitWithExitCode 1 -fi - if [[ `uname -s` == "Darwin" ]]; then NL=$'\\\n' TB='' @@ -140,18 +134,20 @@ PackageSources+="$IFS" PackageSources+=$(grep -oh '"darc-int-[^"]*"' $ConfigFile | tr -d '"') IFS=$PrevIFS -for FeedName in ${PackageSources[@]} ; do - # Check if there is no existing credential for this FeedName - grep -i "<$FeedName>" $ConfigFile - if [ "$?" != "0" ]; then - echo "Adding credentials for $FeedName." +if [ "$CredToken" ]; then + for FeedName in ${PackageSources[@]} ; do + # Check if there is no existing credential for this FeedName + grep -i "<$FeedName>" $ConfigFile + if [ "$?" != "0" ]; then + echo "Adding credentials for $FeedName." - PackageSourceCredentialsNodeFooter="" - NewCredential="${TB}${TB}<$FeedName>${NL}${NL}${NL}" + PackageSourceCredentialsNodeFooter="" + NewCredential="${TB}${TB}<$FeedName>${NL}${NL}${NL}" - sed -i.bak "s|$PackageSourceCredentialsNodeFooter|$NewCredential${NL}$PackageSourceCredentialsNodeFooter|" $ConfigFile - fi -done + sed -i.bak "s|$PackageSourceCredentialsNodeFooter|$NewCredential${NL}$PackageSourceCredentialsNodeFooter|" $ConfigFile + fi + done +fi # Re-enable any entries in disabledPackageSources where the feed name contains darc-int grep -i "" $ConfigFile diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index dc3bd560a50e2..74872895d51cd 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -1,5 +1,5 @@ parameters: -# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job +# Job schema parameters - https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job cancelTimeoutInMinutes: '' condition: '' container: '' @@ -83,7 +83,7 @@ jobs: - name: EnableRichCodeNavigation value: 'true' # Retry signature validation up to three times, waiting 2 seconds between attempts. - # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures + # See https://learn.microsoft.com/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY value: 3,2000 - ${{ each variable in parameters.variables }}: @@ -200,29 +200,28 @@ jobs: publishArtifacts: false # Publish test results - - ${{ if and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')) }}: - - ${{ if eq(parameters.testResultsFormat, 'xunit') }}: - - task: PublishTestResults@2 - displayName: Publish XUnit Test Results - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - ${{ if eq(parameters.testResultsFormat, 'vstest') }}: - - task: PublishTestResults@2 - displayName: Publish TRX Test Results - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() + - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: + - task: PublishTestResults@2 + displayName: Publish XUnit Test Results + inputs: + testResultsFormat: 'xUnit' + testResultsFiles: '*.xml' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: + - task: PublishTestResults@2 + displayName: Publish TRX Test Results + inputs: + testResultsFormat: 'VSTest' + testResultsFiles: '*.trx' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() # gather artifacts - ${{ if ne(parameters.artifacts.publish, '') }}: @@ -246,6 +245,8 @@ jobs: SourceFolder: 'artifacts/log' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log' + continueOnError: true + condition: always() - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - task: CopyFiles@2 @@ -254,6 +255,8 @@ jobs: SourceFolder: 'artifacts/log/$(_BuildConfig)' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + continueOnError: true + condition: always() - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - task: CopyFiles@2 displayName: Gather buildconfiguration for build retry @@ -261,6 +264,7 @@ jobs: SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' - + continueOnError: true + condition: always() - ${{ each step in parameters.artifactPublishSteps }}: - ${{ step }} diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml index 00feec8ebbc3a..84025d0bfe2ff 100644 --- a/eng/common/core-templates/job/onelocbuild.yml +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -2,7 +2,7 @@ parameters: # Optional: dependencies of the job dependsOn: '' - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + # Optional: A defined YAML pool - https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool pool: '' CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 8fe9299542c53..8b72fc5122b42 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -13,7 +13,7 @@ parameters: # Optional: Include PublishBuildArtifacts task enablePublishBuildArtifacts: false - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + # Optional: A defined YAML pool - https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool pool: {} # Optional: should run as a public build even in the internal project diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index c0ce4b3c86186..c4713c8b6ede8 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -33,6 +33,12 @@ parameters: is1ESPipeline: '' + # If set to true and running on a non-public project, + # Internal nuget and blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} displayName: Source-Build (${{ parameters.platform.name }}) @@ -74,6 +80,13 @@ jobs: - ${{ if eq(parameters.is1ESPipeline, '') }}: - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + - ${{ if eq(parameters.enableInternalSources, true) }}: + - template: /eng/common/core-templates/steps/enable-internal-sources.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/steps/enable-internal-runtimes.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} - template: /eng/common/core-templates/steps/source-build.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml index f2144252cc65c..0ca1f8019ef08 100644 --- a/eng/common/core-templates/jobs/codeql-build.yml +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -1,7 +1,7 @@ parameters: # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md continueOnError: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + # Required: A collection of jobs to run - https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job jobs: [] # Optional: if specified, restore and use this version of Guardian instead of the default. overrideGuardianVersion: '' diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index ea69be4341c62..a7e082b4ff989 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -21,7 +21,7 @@ parameters: # Optional: Include toolset dependencies in the generated graph files includeToolset: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + # Required: A collection of jobs to run - https://learn.microsoft.com/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job jobs: [] # Optional: Override automatically derived dependsOn value for "publish build assets" job diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml index d8e5d00852268..a10ccfbee6de6 100644 --- a/eng/common/core-templates/jobs/source-build.yml +++ b/eng/common/core-templates/jobs/source-build.yml @@ -23,6 +23,12 @@ parameters: is1ESPipeline: '' + # If set to true and running on a non-public project, + # Internal nuget and blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - ${{ if ne(parameters.allCompletedJobId, '') }}: @@ -41,6 +47,7 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} + enableInternalSources: ${{ parameters.enableInternalSources }} - ${{ if eq(length(parameters.platforms), 0) }}: - template: /eng/common/core-templates/job/source-build.yml @@ -48,3 +55,4 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ parameters.defaultManagedPlatform }} + enableInternalSources: ${{ parameters.enableInternalSources }} diff --git a/eng/common/core-templates/steps/enable-internal-runtimes.yml b/eng/common/core-templates/steps/enable-internal-runtimes.yml new file mode 100644 index 0000000000000..6bdbf62ac500f --- /dev/null +++ b/eng/common/core-templates/steps/enable-internal-runtimes.yml @@ -0,0 +1,32 @@ +# Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' +# variable with the base64-encoded SAS token, by default + +parameters: +- name: federatedServiceConnection + type: string + default: 'dotnetbuilds-internal-read' +- name: outputVariableName + type: string + default: 'dotnetbuilds-internal-container-read-token-base64' +- name: expiryInHours + type: number + default: 1 +- name: base64Encode + type: boolean + default: true +- name: is1ESPipeline + type: boolean + default: false + +steps: +- ${{ if ne(variables['System.TeamProject'], 'public') }}: + - template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + federatedServiceConnection: ${{ parameters.federatedServiceConnection }} + outputVariableName: ${{ parameters.outputVariableName }} + expiryInHours: ${{ parameters.expiryInHours }} + base64Encode: ${{ parameters.base64Encode }} + storageAccount: dotnetbuilds + container: internal + permissions: rl + is1ESPipeline: ${{ parameters.is1ESPipeline }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/enable-internal-sources.yml b/eng/common/core-templates/steps/enable-internal-sources.yml new file mode 100644 index 0000000000000..80deddafb1b64 --- /dev/null +++ b/eng/common/core-templates/steps/enable-internal-sources.yml @@ -0,0 +1,35 @@ +parameters: +# This is the Azure federated service connection that we log into to get an access token. +- name: nugetFederatedServiceConnection + type: string + default: 'dnceng-artifacts-feeds-read' +- name: is1ESPipeline + type: boolean + default: false + +steps: +- ${{ if ne(variables['System.TeamProject'], 'public') }}: + # If running on dnceng (internal project), just use the default behavior for NuGetAuthenticate. + # If running on DevDiv, NuGetAuthenticate is not really an option. It's scoped to a single feed, and we have many feeds that + # may be added. Instead, we'll use the traditional approach (add cred to nuget.config), but use an account token. + - ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - task: PowerShell@2 + displayName: Setup Internal Feeds + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config + - task: NuGetAuthenticate@1 + - ${{ else }}: + - template: /eng/common/templates/steps/get-federated-access-token.yml + parameters: + federatedServiceConnection: ${{ parameters.nugetFederatedServiceConnection }} + outputVariableName: 'dnceng-artifacts-feeds-read-access-token' + - task: PowerShell@2 + displayName: Setup Internal Feeds + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $(dnceng-artifacts-feeds-read-access-token) + # This is required in certain scenarios to install the ADO credential provider. + # It installed by default in some msbuild invocations (e.g. VS msbuild), but needs to be installed for others + # (e.g. dotnet msbuild). + - task: NuGetAuthenticate@1 diff --git a/eng/common/templates/steps/get-delegate-sas.yml b/eng/common/core-templates/steps/get-delegation-sas.yml similarity index 96% rename from eng/common/templates/steps/get-delegate-sas.yml rename to eng/common/core-templates/steps/get-delegation-sas.yml index c0e8f91317f07..d2901470a7f0b 100644 --- a/eng/common/templates/steps/get-delegate-sas.yml +++ b/eng/common/core-templates/steps/get-delegation-sas.yml @@ -16,6 +16,9 @@ parameters: - name: permissions type: string default: 'rl' +- name: is1ESPipeline + type: boolean + default: false steps: - task: AzureCLI@2 diff --git a/eng/common/core-templates/steps/get-federated-access-token.yml b/eng/common/core-templates/steps/get-federated-access-token.yml new file mode 100644 index 0000000000000..c8c49cc0e8f0f --- /dev/null +++ b/eng/common/core-templates/steps/get-federated-access-token.yml @@ -0,0 +1,28 @@ +parameters: +- name: federatedServiceConnection + type: string +- name: outputVariableName + type: string +# Resource to get a token for. Common values include: +# - '499b84ac-1321-427f-aa17-267ca6975798' for Azure DevOps +# - 'https://storage.azure.com/' for storage +# Defaults to Azure DevOps +- name: resource + type: string + default: '499b84ac-1321-427f-aa17-267ca6975798' + +steps: +- task: AzureCLI@2 + displayName: 'Getting federated access token for feeds' + inputs: + azureSubscription: ${{ parameters.federatedServiceConnection }} + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $accessToken = az account get-access-token --query accessToken --resource ${{ parameters.resource }} --output tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to get access token for resource '${{ parameters.resource }}'" + exit 1 + } + Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" + Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$accessToken" \ No newline at end of file diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index bdd725b496f91..16c778d92cb51 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -19,18 +19,10 @@ steps: set -x df -h - # If building on the internal project, the artifact feeds variable may be available (usually only if needed) - # In that case, call the feed setup script to add internal feeds corresponding to public ones. - # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. - # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those - # changes. + # If file changes are detected, set CopyWipIntoInnerSourceBuildRepo to copy the WIP changes into the inner source build repo. internalRestoreArgs= - if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then - # Temporarily work around https://github.com/dotnet/arcade/issues/7709 - chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) + if ! git diff --quiet; then internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. # This only works if there is a username/email configured, which won't be the case in most CI runs. git config --get user.email @@ -123,6 +115,7 @@ steps: artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) continueOnError: true condition: succeededOrFailed() + sbomEnabled: false # we don't need SBOM for logs # Manually inject component detection so that we can ignore the source build upstream cache, which contains # a nupkg cache of input packages (a local feed). diff --git a/eng/common/cross/armv6/sources.list.bookworm b/eng/common/cross/armv6/sources.list.bookworm new file mode 100644 index 0000000000000..10161135265cf --- /dev/null +++ b/eng/common/cross/armv6/sources.list.bookworm @@ -0,0 +1,2 @@ +deb http://raspbian.raspberrypi.org/raspbian/ bookworm main contrib non-free rpi +deb-src http://raspbian.raspberrypi.org/raspbian/ bookworm main contrib non-free rpi diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index a8e35df7cee14..7455dcb6af4d1 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -314,6 +314,13 @@ while :; do bullseye) # Debian 11 __CodeName=bullseye + if [[ -z "$__UbuntuRepo" ]]; then + __UbuntuRepo="http://ftp.debian.org/debian/" + fi + ;; + bookworm) # Debian 12 + __CodeName=bookworm + if [[ -z "$__UbuntuRepo" ]]; then __UbuntuRepo="http://ftp.debian.org/debian/" fi diff --git a/eng/common/cross/tizen-fetch.sh b/eng/common/cross/tizen-fetch.sh index c15c5066950d1..28936ceef3a71 100755 --- a/eng/common/cross/tizen-fetch.sh +++ b/eng/common/cross/tizen-fetch.sh @@ -7,7 +7,7 @@ fi Log() { - if [ $VERBOSE -ge $1 ]; then + if [ $VERBOSE -ge 1 ]; then echo ${@:2} fi } diff --git a/eng/common/dotnet-install.sh b/eng/common/dotnet-install.sh index 7e69e3a9e24a7..a2fba4703806e 100755 --- a/eng/common/dotnet-install.sh +++ b/eng/common/dotnet-install.sh @@ -82,7 +82,7 @@ if [[ $architecture != "" ]] && [[ $architecture != $buildarch ]]; then dotnetRoot="$dotnetRoot/$architecture" fi -InstallDotNet $dotnetRoot $version "$architecture" $runtime true $runtimeSourceFeed $runtimeSourceFeedKey || { +InstallDotNet "$dotnetRoot" $version "$architecture" $runtime true $runtimeSourceFeed $runtimeSourceFeedKey || { local exit_code=$? Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "dotnet-install.sh failed (exit code '$exit_code')." >&2 ExitWithExitCode $exit_code diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md index c114bc28dcb95..48954b48b8dae 100644 --- a/eng/common/template-guidance.md +++ b/eng/common/template-guidance.md @@ -20,7 +20,7 @@ The `templateIs1ESManaged` is available on most templates and affects which of t ## Multiple outputs -1ES pipeline templates impose a policy where every publish artifact execution results in additional security scans being injected into your pipeline. When using `templates-official/jobs/jobs.yml`, Arcade reduces the number of additional security injections by gathering all publishing outputs into the [Build.ArtifactStagingDirectory](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services), and utilizing the [outputParentDirectory](https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/outputs#multiple-outputs) feature of 1ES pipeline templates. When implementing your pipeline, if you ensure publish artifacts are located in the `$(Build.ArtifactStagingDirectory)`, and utilize the 1ES provided template context, then you can reduce the number of security scans for your pipeline. +1ES pipeline templates impose a policy where every publish artifact execution results in additional security scans being injected into your pipeline. When using `templates-official/jobs/jobs.yml`, Arcade reduces the number of additional security injections by gathering all publishing outputs into the [Build.ArtifactStagingDirectory](https://learn.microsoft.com/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services), and utilizing the [outputParentDirectory](https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/outputs#multiple-outputs) feature of 1ES pipeline templates. When implementing your pipeline, if you ensure publish artifacts are located in the `$(Build.ArtifactStagingDirectory)`, and utilize the 1ES provided template context, then you can reduce the number of security scans for your pipeline. Example: ``` yaml diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 4724e9aaa8091..0c2928d5c799e 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -23,6 +23,7 @@ jobs: displayName: 'Publish logs' continueOnError: true condition: always() + sbomEnabled: false # we don't need SBOM for logs - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: - output: buildArtifacts @@ -32,13 +33,15 @@ jobs: ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} continueOnError: true condition: always() + sbomEnabled: false # we don't need SBOM for logs - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - output: pipelineArtifact targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/eng/common/BuildConfiguration' artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' - continueOnError: true + continueOnError: true + sbomEnabled: false # we don't need SBOM for BuildConfiguration - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - output: pipelineArtifact diff --git a/eng/common/templates-official/steps/enable-internal-runtimes.yml b/eng/common/templates-official/steps/enable-internal-runtimes.yml new file mode 100644 index 0000000000000..f9dd238c6cd54 --- /dev/null +++ b/eng/common/templates-official/steps/enable-internal-runtimes.yml @@ -0,0 +1,9 @@ +# Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' +# variable with the base64-encoded SAS token, by default +steps: +- template: /eng/common/core-templates/steps/enable-internal-runtimes.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/enable-internal-sources.yml b/eng/common/templates-official/steps/enable-internal-sources.yml new file mode 100644 index 0000000000000..e6d57182284df --- /dev/null +++ b/eng/common/templates-official/steps/enable-internal-sources.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/enable-internal-sources.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml new file mode 100644 index 0000000000000..c5a9c1f8275c5 --- /dev/null +++ b/eng/common/templates-official/steps/get-delegation-sas.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/get-federated-access-token.yml b/eng/common/templates-official/steps/get-federated-access-token.yml new file mode 100644 index 0000000000000..c8dcf6b813920 --- /dev/null +++ b/eng/common/templates-official/steps/get-federated-access-token.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/get-federated-access-token.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml index d71eb0c743986..172f9f0fdc970 100644 --- a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -23,4 +23,6 @@ steps: ${{ if parameters.args.artifactName }}: artifactName: ${{ parameters.args.artifactName }} ${{ if parameters.args.properties }}: - properties: ${{ parameters.args.properties }} \ No newline at end of file + properties: ${{ parameters.args.properties }} + ${{ if parameters.args.sbomEnabled }}: + sbomEnabled: ${{ parameters.args.sbomEnabled }} diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 1cf9a6d48127b..5920952c5ba69 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -37,6 +37,7 @@ jobs: displayName: 'Publish logs' continueOnError: true condition: always() + sbomEnabled: false # we don't need SBOM for logs - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - template: /eng/common/core-templates/steps/publish-build-artifacts.yml @@ -59,3 +60,4 @@ jobs: artifactName: 'BuildConfiguration' displayName: 'Publish build retry configuration' continueOnError: true + sbomEnabled: false # we don't need SBOM for BuildConfiguration diff --git a/eng/common/templates/steps/enable-internal-runtimes.yml b/eng/common/templates/steps/enable-internal-runtimes.yml new file mode 100644 index 0000000000000..b21a8038cc1cb --- /dev/null +++ b/eng/common/templates/steps/enable-internal-runtimes.yml @@ -0,0 +1,10 @@ +# Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' +# variable with the base64-encoded SAS token, by default + +steps: +- template: /eng/common/core-templates/steps/enable-internal-runtimes.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/enable-internal-sources.yml b/eng/common/templates/steps/enable-internal-sources.yml new file mode 100644 index 0000000000000..5f87e9abb8aaa --- /dev/null +++ b/eng/common/templates/steps/enable-internal-sources.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/enable-internal-sources.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/steps/get-delegation-sas.yml b/eng/common/templates/steps/get-delegation-sas.yml new file mode 100644 index 0000000000000..83760c9798e36 --- /dev/null +++ b/eng/common/templates/steps/get-delegation-sas.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/get-federated-access-token.yml b/eng/common/templates/steps/get-federated-access-token.yml new file mode 100644 index 0000000000000..31e151d9d9e79 --- /dev/null +++ b/eng/common/templates/steps/get-federated-access-token.yml @@ -0,0 +1,7 @@ +steps: +- template: /eng/common/core-templates/steps/get-federated-access-token.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 0febe696dbdbc..204cb54af051b 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -520,7 +520,7 @@ function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) { # Two part minimal VS version, e.g. "15.9", "16.0", etc. # "components": ["componentId1", "componentId2", ...] # Array of ids of workload components that must be available in the VS instance. -# See e.g. https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-enterprise?view=vs-2017 +# See e.g. https://learn.microsoft.com/visualstudio/install/workload-component-id-vs-enterprise?view=vs-2017 # # Returns JSON describing the located VS instance (same format as returned by vswhere), # or $null if no instance meeting the requirements is found on the machine. diff --git a/eng/common/tools.sh b/eng/common/tools.sh index db64e298ff631..a4f5d1b7761b4 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -438,7 +438,7 @@ function StopProcesses { } function MSBuild { - local args=$@ + local args=( "$@" ) if [[ "$pipelines_log" == true ]]; then InitializeBuildTool InitializeToolset @@ -473,7 +473,7 @@ function MSBuild { args+=( "-logger:$selectedPath" ) fi - MSBuild-Core ${args[@]} + MSBuild-Core "${args[@]}" } function MSBuild-Core { diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index ea2ace1672750..8decaa84fe9ad 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -520,7 +520,7 @@ if (CLR_CMAKE_HOST_UNIX) # Disable frame pointer optimizations so profilers can get better call stacks add_compile_options(-fno-omit-frame-pointer) - # The -fms-extensions enable the stuff like __if_exists, __declspec(uuid()), etc. + # The -fms-extensions enable the stuff like __declspec(uuid()), etc. add_compile_options(-fms-extensions) #-fms-compatibility Enable full Microsoft Visual C++ compatibility #-fms-extensions Accept some non-standard constructs supported by the Microsoft compiler diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index 13da1c03fbaf4..1fe318e984fa8 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -187,6 +187,10 @@ jobs: arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token env: Token: $(dn-bot-dnceng-artifact-feeds-rw) + # Run the NuGetAuthenticate task after the internal feeds are added to the nuget.config + # This ensures that creds are set appropriately for all feeds in the config, and that the + # credential provider is installed. + - task: NuGetAuthenticate@1 - ${{ each monoCrossAOTTargetOS in parameters.monoCrossAOTTargetOS }}: - task: DownloadPipelineArtifact@2 diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 49f9ba6e3f240..23370eabe21a6 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -571,7 +571,7 @@ jobs: targetRid: android-x64 platform: android_x64 shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic + container: android jobParameters: runtimeFlavor: mono buildConfig: ${{ parameters.buildConfig }} @@ -611,7 +611,7 @@ jobs: targetRid: android-x86 platform: android_x86 shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic + container: android jobParameters: runtimeFlavor: mono buildConfig: ${{ parameters.buildConfig }} @@ -631,7 +631,7 @@ jobs: targetRid: android-arm platform: android_arm shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic + container: android jobParameters: runtimeFlavor: mono buildConfig: ${{ parameters.buildConfig }} @@ -651,7 +651,7 @@ jobs: targetRid: android-arm64 platform: android_arm64 shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic + container: android jobParameters: runtimeFlavor: mono buildConfig: ${{ parameters.buildConfig }} diff --git a/eng/pipelines/common/templates/pipeline-with-resources.yml b/eng/pipelines/common/templates/pipeline-with-resources.yml index bf668d6441fe6..ffeafcdde7d2f 100644 --- a/eng/pipelines/common/templates/pipeline-with-resources.yml +++ b/eng/pipelines/common/templates/pipeline-with-resources.yml @@ -47,12 +47,16 @@ extends: ROOTFS_DIR: /crossrootfs/arm64 # This container contains all required toolsets to build for Android and for Linux with bionic libc. + android: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-cross-android-amd64-net9.0 + + # This container contains all required toolsets to build for Android and for Linux with bionic libc and a special layout of OpenSSL. linux_bionic: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-android-amd64 + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-android-openssl-net9.0 # This container contains all required toolsets to build for Android as well as tooling to build docker images. android_docker: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-android-docker + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-android-docker-net9.0 linux_x64: image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-cross-amd64-net9.0 @@ -84,12 +88,12 @@ extends: image: mcr.microsoft.com/dotnet-buildtools/prereqs:almalinux-8-source-build linux_s390x: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-s390x + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-cross-s390x-net9.0 env: ROOTFS_DIR: /crossrootfs/s390x linux_ppc64le: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-ppc64le + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-cross-ppc64le-net9.0 env: ROOTFS_DIR: /crossrootfs/ppc64le @@ -105,17 +109,17 @@ extends: image: mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8 browser_wasm: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-webassembly + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-webassembly-amd64-net9.0 env: ROOTFS_DIR: /crossrootfs/x64 wasi_wasm: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-webassembly + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-webassembly-amd64-net9.0 env: ROOTFS_DIR: /crossrootfs/x64 freebsd_x64: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-cross-amd64-freebsd-13 + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-cross-freebsd-13-net9.0 env: ROOTFS_DIR: /crossrootfs/x64 @@ -128,4 +132,4 @@ extends: image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-debpkg rpmpkg: - image: mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-fpm + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-fpm-net9.0 diff --git a/eng/pipelines/common/templates/runtimes/build-test-job.yml b/eng/pipelines/common/templates/runtimes/build-test-job.yml index c86e4e04f1116..bbc9f854801ff 100644 --- a/eng/pipelines/common/templates/runtimes/build-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/build-test-job.yml @@ -44,7 +44,7 @@ jobs: displayName: '${{ parameters.runtimeFlavor }} Common Pri1 Test Build AnyOS AnyCPU ${{ parameters.buildConfig }}' # Since the condition is being altered, merge the default with the additional conditions. - # See https://docs.microsoft.com/azure/devops/pipelines/process/conditions + # See https://learn.microsoft.com/azure/devops/pipelines/process/conditions condition: and(succeeded(), ${{ parameters.condition }}) ${{ if ne(parameters.dependsOn[0], '') }}: diff --git a/eng/pipelines/common/templates/template1es.yml b/eng/pipelines/common/templates/template1es.yml index 0964295750d25..e629a74646e59 100644 --- a/eng/pipelines/common/templates/template1es.yml +++ b/eng/pipelines/common/templates/template1es.yml @@ -15,7 +15,7 @@ resources: - repository: 1ESPipelineTemplates type: git name: 1ESPipelineTemplates/1ESPipelineTemplates - ref: refs/tags/release-2024-05-13-1 + ref: refs/tags/release extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates diff --git a/eng/pipelines/coreclr/templates/run-performance-job.yml b/eng/pipelines/coreclr/templates/run-performance-job.yml index 2b61558e2fc97..b99646881eb87 100644 --- a/eng/pipelines/coreclr/templates/run-performance-job.yml +++ b/eng/pipelines/coreclr/templates/run-performance-job.yml @@ -86,7 +86,7 @@ jobs: ${V8_ENGINE_PATH} -e 'console.log(`V8 version: ${this.version()}`)' - ${{ if ne(parameters.runtimeType, 'wasm') }}: - HelixPreCommandsWasmOnLinux: echo - - HelixPreCommandStemWindows: 'set ORIGPYPATH=%PYTHONPATH%;py -m pip install -U pip;py -3 -m venv %HELIX_WORKITEM_PAYLOAD%\.venv;call %HELIX_WORKITEM_PAYLOAD%\.venv\Scripts\activate.bat;set PYTHONPATH=;py -3 -m pip install -U pip;py -3 -m pip install urllib3==1.26.15;py -3 -m pip install azure.storage.blob==12.0.0;py -3 -m pip install azure.storage.queue==12.0.0;set "PERFLAB_UPLOAD_TOKEN=$(HelixPerfUploadTokenValue)"' + - HelixPreCommandStemWindows: 'set ORIGPYPATH=%PYTHONPATH%;py -m pip install -U pip;py -3 -m venv %HELIX_WORKITEM_PAYLOAD%\.venv;call %HELIX_WORKITEM_PAYLOAD%\.venv\Scripts\activate.bat;set PYTHONPATH=;py -3 -m pip install -U pip;py -3 -m pip install urllib3==1.26.15;py -3 -m pip install azure.storage.blob==12.0.0;py -3 -m pip install azure.storage.queue==12.0.0;py -3 -m pip install azure.identity==1.16.0;set "PERFLAB_UPLOAD_TOKEN=$(HelixPerfUploadTokenValue)"' - HelixPreCommandStemLinux: >- export ORIGPYPATH=$PYTHONPATH export CRYPTOGRAPHY_ALLOW_OPENSSL_102=true; @@ -104,13 +104,14 @@ jobs: pip3 install urllib3==1.26.15 && pip3 install --user azure.storage.blob==12.0.0 && pip3 install --user azure.storage.queue==12.0.0 && + pip3 install --user azure.identity==1.16.0 && sudo apt-get update && sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates && $(HelixPreCommandsWasmOnLinux) && export PERFLAB_UPLOAD_TOKEN="$(HelixPerfUploadTokenValue)" || export PERF_PREREQS_INSTALL_FAILED=1; test "x$PERF_PREREQS_INSTALL_FAILED" = "x1" && echo "** Error: Failed to install prerequites **" - - HelixPreCommandStemMusl: 'ulimit -n 4096;export ORIGPYPATH=$PYTHONPATH;sudo apk add icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib cargo;sudo apk add libgdiplus --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing; python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install urllib3==1.26.15;pip3 install azure.storage.blob==12.7.1;pip3 install azure.storage.queue==12.1.5;export PERFLAB_UPLOAD_TOKEN="$(HelixPerfUploadTokenValue)"' + - HelixPreCommandStemMusl: 'ulimit -n 4096;export ORIGPYPATH=$PYTHONPATH;sudo apk add icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib cargo;sudo apk add libgdiplus --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing; python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install urllib3==1.26.15;pip3 install azure.storage.blob==12.7.1;pip3 install azure.storage.queue==12.1.5;pip3 install azure.identity==1.16.0;export PERFLAB_UPLOAD_TOKEN="$(HelixPerfUploadTokenValue)"' - ExtraMSBuildLogsWindows: 'set MSBUILDDEBUGCOMM=1;set "MSBUILDDEBUGPATH=%HELIX_WORKITEM_UPLOAD_ROOT%"' - ExtraMSBuildLogsLinux: 'export MSBUILDDEBUGCOMM=1;export "MSBUILDDEBUGPATH=$HELIX_WORKITEM_UPLOAD_ROOT"' - HelixPreCommand: '' diff --git a/eng/pipelines/coreclr/templates/run-scenarios-job.yml b/eng/pipelines/coreclr/templates/run-scenarios-job.yml index 78db1ae8cbd09..c2cf7ad7fe642 100644 --- a/eng/pipelines/coreclr/templates/run-scenarios-job.yml +++ b/eng/pipelines/coreclr/templates/run-scenarios-job.yml @@ -60,16 +60,16 @@ jobs: - SharedHelixPreCommands: 'chmod +x $HELIX_WORKITEM_PAYLOAD/machine-setup.sh;. $HELIX_WORKITEM_PAYLOAD/machine-setup.sh;export PYTHONPATH=$HELIX_WORKITEM_PAYLOAD/scripts:$HELIX_WORKITEM_PAYLOAD' - ${{ if eq(parameters.osGroup, 'windows') }}: - - HelixPreCommandWindows: 'set ORIGPYPATH=%PYTHONPATH%;py -3 -m venv %HELIX_WORKITEM_PAYLOAD%\.venv;call %HELIX_WORKITEM_PAYLOAD%\.venv\Scripts\activate.bat;set PYTHONPATH=;py -3 -m pip install -U pip;py -3 -m pip install --user azure.storage.blob==12.0.0;py -3 -m pip install --user azure.storage.queue==12.0.0;py -3 -m pip install --user urllib3==1.26.15;set "PERFLAB_UPLOAD_TOKEN=$(PerfCommandUploadToken)"' + - HelixPreCommandWindows: 'set ORIGPYPATH=%PYTHONPATH%;py -3 -m venv %HELIX_WORKITEM_PAYLOAD%\.venv;call %HELIX_WORKITEM_PAYLOAD%\.venv\Scripts\activate.bat;set PYTHONPATH=;py -3 -m pip install -U pip;py -3 -m pip install --user azure.storage.blob==12.0.0;py -3 -m pip install --user azure.storage.queue==12.0.0;py -3 -m pip install --user urllib3==1.26.15;py -3 -m pip install --user azure.identity==1.16.0;set "PERFLAB_UPLOAD_TOKEN=$(PerfCommandUploadToken)"' - HelixPostCommandsWindows: 'set PYTHONPATH=%ORIGPYPATH%' - ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.osGroup, 'osx'), ne(parameters.osSubGroup, '_musl')) }}: - - HelixPreCommandLinux: 'export ORIGPYPATH=$PYTHONPATH;export CRYPTOGRAPHY_ALLOW_OPENSSL_102=true;sudo apt-get -y install python3-venv;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install --user azure.storage.blob==12.0.0;pip3 install --user azure.storage.queue==12.0.0;pip3 install --user urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' + - HelixPreCommandLinux: 'export ORIGPYPATH=$PYTHONPATH;export CRYPTOGRAPHY_ALLOW_OPENSSL_102=true;sudo apt-get -y install python3-venv;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install --user azure.storage.blob==12.0.0;pip3 install --user azure.storage.queue==12.0.0;pip3 install --user azure.identity==1.16.0;pip3 install --user urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' - HelixPostCommandsLinux: 'export PYTHONPATH=$ORIGPYPATH' - ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.osGroup, 'osx'), eq(parameters.osSubGroup, '_musl')) }}: - - HelixPreCommandMusl: 'ulimit -n 4096;export ORIGPYPATH=$PYTHONPATH;sudo apk add py3-virtualenv;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install --user azure.storage.blob==12.0.0;pip3 install --user azure.storage.queue==12.0.0;pip3 install --user urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' + - HelixPreCommandMusl: 'ulimit -n 4096;export ORIGPYPATH=$PYTHONPATH;sudo apk add py3-virtualenv;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install --user azure.storage.blob==12.0.0;pip3 install --user azure.storage.queue==12.0.0;pip3 install --user azure.identity==1.16.0;pip3 install --user urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' - HelixPostCommandsMusl: 'export PYTHONPATH=$ORIGPYPATH' - ${{ if eq(parameters.osGroup, 'osx') }}: - - HelixPreCommandOSX: 'export ORIGPYPATH=$PYTHONPATH;export CRYPTOGRAPHY_ALLOW_OPENSSL_102=true;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install azure.storage.blob==12.0.0;pip3 install azure.storage.queue==12.0.0;pip3 install urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' + - HelixPreCommandOSX: 'export ORIGPYPATH=$PYTHONPATH;export CRYPTOGRAPHY_ALLOW_OPENSSL_102=true;python3 -m venv $HELIX_WORKITEM_PAYLOAD/.venv;source $HELIX_WORKITEM_PAYLOAD/.venv/bin/activate;export PYTHONPATH=;python3 -m pip install -U pip;pip3 install azure.storage.blob==12.0.0;pip3 install azure.storage.queue==12.0.0;pip3 install azure.identity==1.16.0;pip3 install urllib3==1.26.15;export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' - HelixPostCommandOSX: 'export PYTHONPATH=$ORIGPYPATH' # extra private job settings diff --git a/eng/pipelines/libraries/run-test-job.yml b/eng/pipelines/libraries/run-test-job.yml index bc3697359f044..82017494a4bb5 100644 --- a/eng/pipelines/libraries/run-test-job.yml +++ b/eng/pipelines/libraries/run-test-job.yml @@ -104,6 +104,10 @@ jobs: arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token env: Token: $(dn-bot-dnceng-artifact-feeds-rw) + # Run the NuGetAuthenticate task after the internal feeds are added to the nuget.config + # This ensures that creds are set appropriately for all feeds in the config, and that the + # credential provider is installed. + - task: NuGetAuthenticate@1 - ${{ if in(parameters.osGroup, 'osx', 'maccatalyst', 'ios', 'iossimulator', 'tvos', 'tvossimulator') }}: - script: $(Build.SourcesDirectory)/eng/install-native-dependencies.sh ${{ parameters.osGroup }} diff --git a/eng/testing/debug-dump-template.md b/eng/testing/debug-dump-template.md index c8759600430f0..daf765c0c293b 100644 --- a/eng/testing/debug-dump-template.md +++ b/eng/testing/debug-dump-template.md @@ -41,7 +41,7 @@ You can read the rest of the document for information purposes (there is useful # Install SOS debugging extension -Now use the [dotnet-sos global tool](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-sos) to install the SOS debugging extension. +Now use the [dotnet-sos global tool](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos) to install the SOS debugging extension. ```cmd dotnet tool install --global dotnet-sos dotnet tool update --global dotnet-sos @@ -61,7 +61,7 @@ dotnet sos install --architecture x64 ## ... and you want to debug with WinDbg -Install or update WinDbg if necessary ([external](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools), [internal](https://osgwiki.com/wiki/Installing_WinDbg)). If you don't have a recent WinDbg you may have to do `.update sos`. +Install or update WinDbg if necessary ([external](https://learn.microsoft.com/windows-hardware/drivers/debugger/debugger-download-tools), [internal](https://osgwiki.com/wiki/Installing_WinDbg)). If you don't have a recent WinDbg you may have to do `.update sos`. Open WinDbg and open the dump with `File>Open Dump`. ``` @@ -81,7 +81,7 @@ Currently this is not possible because mscordbi.dll is not signed. ## ... and you want to debug with dotnet-dump -Install the [dotnet-dump global tool](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dump). +Install the [dotnet-dump global tool](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dump). ```cmd dotnet tool install --global dotnet-dump dotnet tool update --global dotnet-dump @@ -129,7 +129,7 @@ loadsymbols ## ... and you want to debug with dotnet-dump -Install the [dotnet-dump global tool](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dump). +Install the [dotnet-dump global tool](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-dump). ```sh dotnet tool install --global dotnet-dump dotnet tool update --global dotnet-dump diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt index 11bfdab2c7414..58c5621f45240 100644 --- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt +++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt @@ -46,4 +46,5 @@ Wasm.Build.Tests.WasmSIMDTests Wasm.Build.Tests.WasmTemplateTests Wasm.Build.Tests.WorkloadTests Wasm.Build.Tests.MT.Blazor.SimpleMultiThreadedTests -Wasm.Build.Tests.TestAppScenarios.DebugLevelTests +Wasm.Build.Tests.TestAppScenarios.WasmSdkDebugLevelTests +Wasm.Build.Tests.TestAppScenarios.WasmAppBuilderDebugLevelTests diff --git a/global.json b/global.json index 1d941dfd8c893..62754257d5c7b 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "9.0.100-preview.4.24267.66" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24272.5", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24272.5", - "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24272.5", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24281.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24281.1", + "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24281.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "9.0.0-preview.5.24272.3" diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index a37abf7a0c116..1586ba4132120 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -237,7 +237,9 @@ Common\System\Collections\Generic\ArrayBuilder.cs - + + + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 7c3cad7d86637..b84c9aec7d68f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -816,11 +816,7 @@ public unsafe void Initialize() RuntimeType arrayType = (RuntimeType)GetType(); - if (arrayType.GenericCache is not ArrayInitializeCache cache) - { - cache = new ArrayInitializeCache(arrayType); - arrayType.GenericCache = cache; - } + ArrayInitializeCache cache = arrayType.GetOrCreateCacheEntry(); delegate* constructorFtn = cache.ConstructorEntrypoint; ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(this); @@ -833,17 +829,21 @@ public unsafe void Initialize() } } - private sealed unsafe partial class ArrayInitializeCache + internal sealed unsafe partial class ArrayInitializeCache : RuntimeType.IGenericCacheEntry { internal readonly delegate* ConstructorEntrypoint; [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_GetElementConstructorEntrypoint")] private static partial delegate* GetElementConstructorEntrypoint(QCallTypeHandle arrayType); - public ArrayInitializeCache(RuntimeType arrayType) + private ArrayInitializeCache(delegate* constructorEntrypoint) { - ConstructorEntrypoint = GetElementConstructorEntrypoint(new QCallTypeHandle(ref arrayType)); + ConstructorEntrypoint = constructorEntrypoint; } + + public static ArrayInitializeCache Create(RuntimeType arrayType) => new(GetElementConstructorEntrypoint(new QCallTypeHandle(ref arrayType))); + public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._arrayInitializeCache = this; + public static ref ArrayInitializeCache? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) => ref compositeEntry._arrayInitializeCache; } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index cc46e2a75d8b2..9c92874f8ec27 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -87,31 +87,49 @@ private static EnumInfo GetEnumInfo(RuntimeType enumType, bo typeof(TStorage) == typeof(nuint) || typeof(TStorage) == typeof(float) || typeof(TStorage) == typeof(double) || typeof(TStorage) == typeof(char), $"Unexpected {nameof(TStorage)} == {typeof(TStorage)}"); - return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ? + return enumType.FindCacheEntry>() is {} info && (!getNames || info.Names is not null) ? info : InitializeEnumInfo(enumType, getNames); [MethodImpl(MethodImplOptions.NoInlining)] static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames) + { + // If we're asked to get the cache with names, + // force that copy into the cache even if we already have a cache entry without names + // so we don't have to recompute the names if asked again. + return getNames + ? enumType.ReplaceCacheEntry(EnumInfo.Create(enumType, getNames: true)) + : enumType.GetOrCreateCacheEntry>(); + } + } + + internal sealed partial class EnumInfo : RuntimeType.IGenericCacheEntry> + { + public static EnumInfo Create(RuntimeType type, bool getNames) { TStorage[]? values = null; string[]? names = null; GetEnumValuesAndNames( - new QCallTypeHandle(ref enumType), + new QCallTypeHandle(ref type), ObjectHandleOnStack.Create(ref values), ObjectHandleOnStack.Create(ref names), getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); Debug.Assert(values!.GetType() == typeof(TStorage[])); - Debug.Assert(!getNames || names!.GetType() == typeof(string[])); - bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); + bool hasFlagsAttribute = type.IsDefined(typeof(FlagsAttribute), inherit: false); - var entry = new EnumInfo(hasFlagsAttribute, values, names!); - enumType.GenericCache = entry; - return entry; + return new EnumInfo(hasFlagsAttribute, values, names!); } + + public static EnumInfo Create(RuntimeType type) => Create(type, getNames: false); + + public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._enumInfo = this; + + // This type is the only type that will be stored in the _enumInfo field, so we can use Unsafe.As here. + public static ref EnumInfo? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) + => ref Unsafe.As?>(ref compositeEntry._enumInfo); } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 51c60b74f4168..461922f7f017b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -308,15 +308,6 @@ public static object GetUninitializedObject( [LibraryImport(QCall, EntryPoint = "ObjectNative_AllocateUninitializedClone")] internal static partial void AllocateUninitializedClone(ObjectHandleOnStack objHandle); - /// true if given type is reference type or value type that contains references - [Intrinsic] - public static bool IsReferenceOrContainsReferences() - { - // The body of this function will be replaced by the EE with unsafe code!!! - // See getILIntrinsicImplementationForRuntimeHelpers for how this happens. - throw new InvalidOperationException(); - } - /// true if given type is bitwise equatable (memcmp can be used for equality checking) /// /// Only use the result of this for Equals() comparison, not for CompareTo() comparison. @@ -518,32 +509,7 @@ private static unsafe void DispatchTailCalls( if (type.IsNullHandle()) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - TypeHandle handle = type.GetNativeTypeHandle(); - - if (handle.IsTypeDesc) - throw new ArgumentException(SR.Arg_TypeNotSupported); - - MethodTable* pMT = handle.AsMethodTable(); - - if (pMT->ContainsGenericVariables) - throw new ArgumentException(SR.Arg_TypeNotSupported); - - if (pMT->IsValueType) - { - if (pMT->IsByRefLike) - throw new NotSupportedException(SR.NotSupported_ByRefLike); - - if (MethodTable.AreSameType(pMT, (MethodTable*)RuntimeTypeHandle.ToIntPtr(typeof(void).TypeHandle))) - throw new ArgumentException(SR.Arg_TypeNotSupported); - - object? result = Box(pMT, ref target); - GC.KeepAlive(type); - return result; - } - else - { - return Unsafe.As(ref target); - } + return type.GetRuntimeType().Box(ref target); } [LibraryImport(QCall, EntryPoint = "ReflectionInvocation_SizeOf")] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.cs index ef0e80bde52a7..dbeb35b013fad 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.cs @@ -16,7 +16,7 @@ // event sinks are COM objects implementing source interfaces. Once an event sink is passed to the COM // server (through a mechanism known as 'binding/advising to connection point'), COM server will be // calling source interface methods to "fire events". -// See https://docs.microsoft.com/cpp/mfc/connection-points +// See https://learn.microsoft.com/cpp/mfc/connection-points // // There are few interesting obervations about source interfaces. Usually source interfaces are defined // as 'dispinterface' - meaning that only late-bound invocations on this interface are allowed. Even diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 472f4f6318a3d..7eddc635ef76b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -270,24 +270,27 @@ internal static void GetActivationInfo( RuntimeType rt, out delegate* pfnAllocator, out void* vAllocatorFirstArg, - out delegate* pfnCtor, + out delegate* pfnRefCtor, + out delegate* pfnValueCtor, out bool ctorIsPublic) { Debug.Assert(rt != null); delegate* pfnAllocatorTemp = default; void* vAllocatorFirstArgTemp = default; - delegate* pfnCtorTemp = default; + delegate* pfnRefCtorTemp = default; + delegate* pfnValueCtorTemp = default; Interop.BOOL fCtorIsPublicTemp = default; GetActivationInfo( ObjectHandleOnStack.Create(ref rt), &pfnAllocatorTemp, &vAllocatorFirstArgTemp, - &pfnCtorTemp, &fCtorIsPublicTemp); + &pfnRefCtorTemp, &pfnValueCtorTemp, &fCtorIsPublicTemp); pfnAllocator = pfnAllocatorTemp; vAllocatorFirstArg = vAllocatorFirstArgTemp; - pfnCtor = pfnCtorTemp; + pfnRefCtor = pfnRefCtorTemp; + pfnValueCtor = pfnValueCtorTemp; ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE; } @@ -296,7 +299,8 @@ private static partial void GetActivationInfo( ObjectHandleOnStack pRuntimeType, delegate** ppfnAllocator, void** pvAllocatorFirstArg, - delegate** ppfnCtor, + delegate** ppfnRefCtor, + delegate** ppfnValueCtor, Interop.BOOL* pfCtorIsPublic); #if FEATURE_COMINTEROP diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs index 4d73cfad39143..369d3bc5b8637 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs @@ -13,7 +13,7 @@ internal sealed partial class RuntimeType /// A cache which allows optimizing , /// , and related APIs. /// - private sealed unsafe class ActivatorCache + internal sealed unsafe class ActivatorCache : IGenericCacheEntry { // The managed calli to the newobj allocator, plus its first argument (MethodTable*). // In the case of the COM allocator, first arg is ComClassFactory*, not MethodTable*. @@ -21,16 +21,19 @@ private sealed unsafe class ActivatorCache private readonly void* _allocatorFirstArg; // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument. - private readonly delegate* _pfnCtor; + private readonly delegate* _pfnRefCtor; + private readonly delegate* _pfnValueCtor; private readonly bool _ctorIsPublic; - private CreateUninitializedCache? _createUninitializedCache; - #if DEBUG private readonly RuntimeType _originalRuntimeType; #endif - internal ActivatorCache(RuntimeType rt) + public static ActivatorCache Create(RuntimeType type) => new(type); + public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._activatorCache = this; + public static ref ActivatorCache? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) => ref compositeEntry._activatorCache; + + private ActivatorCache(RuntimeType rt) { Debug.Assert(rt != null); @@ -48,7 +51,7 @@ internal ActivatorCache(RuntimeType rt) { RuntimeTypeHandle.GetActivationInfo(rt, out _pfnAllocator!, out _allocatorFirstArg, - out _pfnCtor!, out _ctorIsPublic); + out _pfnRefCtor!, out _pfnValueCtor!, out _ctorIsPublic); } catch (Exception ex) { @@ -87,14 +90,29 @@ internal ActivatorCache(RuntimeType rt) // would have thrown an exception if 'rt' were a normal reference type // without a ctor. - if (_pfnCtor == null) + if (_pfnRefCtor == null) { - static void CtorNoopStub(object? uninitializedObject) { } - _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary + static void RefCtorNoopStub(object? uninitializedObject) { } + _pfnRefCtor = &RefCtorNoopStub; // we use null singleton pattern if no ctor call is necessary Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public } + if (rt.IsValueType) + { + if (_pfnValueCtor == null) + { + static void ValueRefCtorNoopStub(ref byte uninitializedObject) { } + _pfnValueCtor = &ValueRefCtorNoopStub; // we use null singleton pattern if no ctor call is necessary + + Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public + } + } + else + { + Debug.Assert(_pfnValueCtor == null); // Non-value types shouldn't have a value constructor. + } + // We don't need to worry about invoking cctors here. The runtime will figure it // out for us when the instance ctor is called. For value types, because we're // creating a boxed default(T), the static cctor is called when *any* instance @@ -120,15 +138,15 @@ static void CtorNoopStub(object? uninitializedObject) { } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); + internal void CallRefConstructor(object? uninitializedObject) => _pfnRefCtor(uninitializedObject); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal CreateUninitializedCache GetCreateUninitializedCache(RuntimeType rt) + internal void CallValueConstructor(ref byte uninitializedObject) { #if DEBUG - CheckOriginalRuntimeType(rt); + Debug.Assert(_originalRuntimeType.IsValueType); #endif - return _createUninitializedCache ??= new CreateUninitializedCache(rt); + _pfnValueCtor(ref uninitializedObject); } #if DEBUG diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.cs new file mode 100644 index 0000000000000..b687947f4dde8 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.cs @@ -0,0 +1,146 @@ +// 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; +using System.Runtime.InteropServices; + +namespace System +{ + internal sealed partial class RuntimeType + { + /// + /// A cache which allows optimizing . + /// + internal sealed unsafe partial class BoxCache : IGenericCacheEntry + { + public static BoxCache Create(RuntimeType type) => new(type); + public void InitializeCompositeCache(CompositeCacheEntry compositeEntry) => compositeEntry._boxCache = this; + public static ref BoxCache? GetStorageRef(CompositeCacheEntry compositeEntry) => ref compositeEntry._boxCache; + + // The managed calli to the newobj allocator, plus its first argument + private readonly delegate* _pfnAllocator; + private readonly void* _allocatorFirstArg; + private readonly int _nullableValueOffset; + private readonly uint _valueTypeSize; + private readonly MethodTable* _pMT; + +#if DEBUG + private readonly RuntimeType _originalRuntimeType; +#endif + + private BoxCache(RuntimeType rt) + { + Debug.Assert(rt != null); + +#if DEBUG + _originalRuntimeType = rt; +#endif + + TypeHandle handle = rt.TypeHandle.GetNativeTypeHandle(); + + if (handle.IsTypeDesc) + throw new ArgumentException(SR.Arg_TypeNotSupported); + + _pMT = handle.AsMethodTable(); + + // For value types, this is checked in GetBoxInfo, + // but for non-value types, we still need to check this case for consistent behavior. + if (_pMT->ContainsGenericVariables) + throw new ArgumentException(SR.Arg_TypeNotSupported); + + if (_pMT->IsValueType) + { + GetBoxInfo(rt, out _pfnAllocator, out _allocatorFirstArg, out _nullableValueOffset, out _valueTypeSize); + } + } + + internal object? Box(RuntimeType rt, ref byte data) + { +#if DEBUG + if (_originalRuntimeType != rt) + { + Debug.Fail("Caller passed the wrong RuntimeType to this routine." + + Environment.NewLineConst + "Expected: " + (_originalRuntimeType ?? (object)"") + + Environment.NewLineConst + "Actual: " + (rt ?? (object)"")); + } +#endif + if (_pfnAllocator == null) + { + // If the allocator is null, then we shouldn't allocate and make a copy, + // we should return the data as the object it currently is. + return Unsafe.As(ref data); + } + + ref byte source = ref data; + + byte maybeNullableHasValue = Unsafe.ReadUnaligned(ref source); + + if (_nullableValueOffset != 0) + { + if (maybeNullableHasValue == 0) + { + return null; + } + source = ref Unsafe.Add(ref source, _nullableValueOffset); + } + + object result = _pfnAllocator(_allocatorFirstArg); + GC.KeepAlive(rt); + + if (_pMT->ContainsGCPointers) + { + Buffer.BulkMoveWithWriteBarrier(ref result.GetRawData(), ref source, _valueTypeSize); + } + else + { + SpanHelpers.Memmove(ref result.GetRawData(), ref source, _valueTypeSize); + } + + return result; + } + + /// + /// Given a RuntimeType, returns information about how to box instances + /// of it via calli semantics. + /// + private static void GetBoxInfo( + RuntimeType rt, + out delegate* pfnAllocator, + out void* vAllocatorFirstArg, + out int nullableValueOffset, + out uint valueTypeSize) + { + Debug.Assert(rt != null); + + delegate* pfnAllocatorTemp = default; + void* vAllocatorFirstArgTemp = default; + int nullableValueOffsetTemp = default; + uint valueTypeSizeTemp = default; + + GetBoxInfo( + new QCallTypeHandle(ref rt), + &pfnAllocatorTemp, &vAllocatorFirstArgTemp, + &nullableValueOffsetTemp, &valueTypeSizeTemp); + + pfnAllocator = pfnAllocatorTemp; + vAllocatorFirstArg = vAllocatorFirstArgTemp; + nullableValueOffset = nullableValueOffsetTemp; + valueTypeSize = valueTypeSizeTemp; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ReflectionInvocation_GetBoxInfo")] + private static partial void GetBoxInfo( + QCallTypeHandle type, + delegate** ppfnAllocator, + void** pvAllocatorFirstArg, + int* pNullableValueOffset, + uint* pValueTypeSize); + } + + internal object? Box(ref byte data) + { + return GetOrCreateCacheEntry().Box(this, ref data); + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index c6ea76fde9b85..28c03245f6dde 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -1454,11 +1454,8 @@ internal T[] GetMemberList(MemberListType listType, string? name, CacheType cach private static CerHashtable s_methodInstantiations; private static object? s_methodInstantiationsLock; private string? m_defaultMemberName; - // Generic cache for rare scenario specific data. Used for: - // - Enum names and values (EnumInfo) - // - Activator.CreateInstance (ActivatorCache) - // - Array.Initialize (ArrayInitializeCache) - private object? m_genericCache; + // Generic cache for rare scenario specific data. + private IGenericCacheEntry? m_genericCache; private object[]? _emptyArray; // Object array cache for Attribute.GetCustomAttributes() pathological no-result case. private RuntimeType? _genericTypeDefinition; #endregion @@ -1501,30 +1498,31 @@ private MemberInfoCache GetMemberCache(ref MemberInfoCache? m_cache) #region Internal Members + internal ref IGenericCacheEntry? GenericCache => ref m_genericCache; - /// - /// Generic cache for rare scenario specific data. It is used to cache either Enum names, Enum values, - /// the Activator cache or function pointer parameters. - /// - internal object? GenericCache + internal sealed class FunctionPointerCache : IGenericCacheEntry { - get => m_genericCache; - set => m_genericCache = value; + public Type[] FunctionPointerReturnAndParameterTypes { get; } + + private FunctionPointerCache(Type[] functionPointerReturnAndParameterTypes) + { + FunctionPointerReturnAndParameterTypes = functionPointerReturnAndParameterTypes; + } + + public static FunctionPointerCache Create(RuntimeType type) + { + Debug.Assert(type.IsFunctionPointer); + return new(RuntimeTypeHandle.GetArgumentTypesFromFunctionPointer(type)); + } + public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._functionPointerCache = this; + public static ref FunctionPointerCache? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) => ref compositeEntry._functionPointerCache; } internal Type[] FunctionPointerReturnAndParameterTypes { get { - Debug.Assert(m_runtimeType.IsFunctionPointer); - Type[]? value = (Type[]?)GenericCache; - if (value == null) - { - GenericCache = value = RuntimeTypeHandle.GetArgumentTypesFromFunctionPointer(m_runtimeType); - Debug.Assert(value.Length > 0); - } - - return value; + return m_runtimeType.GetOrCreateCacheEntry().FunctionPointerReturnAndParameterTypes; } } @@ -1930,10 +1928,23 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field) return retval; } - internal object? GenericCache + internal T GetOrCreateCacheEntry() + where T : class, IGenericCacheEntry + { + return IGenericCacheEntry.GetOrCreate(this); + } + + internal T? FindCacheEntry() + where T : class, IGenericCacheEntry { - get => CacheIfExists?.GenericCache; - set => Cache.GenericCache = value; + return IGenericCacheEntry.Find(this); + } + + internal T ReplaceCacheEntry(T entry) + where T : class, IGenericCacheEntry + { + IGenericCacheEntry.Replace(this, entry); + return entry; } internal static FieldInfo GetFieldInfo(IRuntimeFieldInfo fieldHandle) @@ -2376,7 +2387,7 @@ private static bool FilterApplyMethodBase( #endregion - #endregion +#endregion #region Private Data Members @@ -3886,22 +3897,7 @@ private void CreateInstanceCheckThis() [DebuggerHidden] internal object GetUninitializedObject() { - object? genericCache = GenericCache; - - if (genericCache is not CreateUninitializedCache cache) - { - if (genericCache is ActivatorCache activatorCache) - { - cache = activatorCache.GetCreateUninitializedCache(this); - } - else - { - cache = new CreateUninitializedCache(this); - GenericCache = cache; - } - } - - return cache.CreateUninitializedObject(this); + return GetOrCreateCacheEntry().CreateUninitializedObject(this); } /// @@ -3914,11 +3910,7 @@ internal object GetUninitializedObject() // Get or create the cached factory. Creating the cache will fail if one // of our invariant checks fails; e.g., no appropriate ctor found. - if (GenericCache is not ActivatorCache cache) - { - cache = new ActivatorCache(this); - GenericCache = cache; - } + ActivatorCache cache = GetOrCreateCacheEntry(); if (!cache.CtorIsPublic && publicOnly) { @@ -3932,7 +3924,7 @@ internal object GetUninitializedObject() object? obj = cache.CreateUninitializedObject(this); try { - cache.CallConstructor(obj); + cache.CallRefConstructor(obj); } catch (Exception e) when (wrapExceptions) { @@ -3942,26 +3934,23 @@ internal object GetUninitializedObject() return obj; } - // Specialized version of the above for Activator.CreateInstance() + // Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance() [DebuggerStepThrough] [DebuggerHidden] internal object? CreateInstanceOfT() { - if (GenericCache is not ActivatorCache cache) - { - cache = new ActivatorCache(this); - GenericCache = cache; - } + ActivatorCache cache = GetOrCreateCacheEntry(); if (!cache.CtorIsPublic) { throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); } + // We reuse ActivatorCache here to ensure that we aren't always creating two entries in the cache. object? obj = cache.CreateUninitializedObject(this); try { - cache.CallConstructor(obj); + cache.CallRefConstructor(obj); } catch (Exception e) { @@ -3971,6 +3960,30 @@ internal object GetUninitializedObject() return obj; } + // Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance() + [DebuggerStepThrough] + [DebuggerHidden] + internal void CallDefaultStructConstructor(ref byte data) + { + Debug.Assert(IsValueType); + + ActivatorCache cache = GetOrCreateCacheEntry(); + + if (!cache.CtorIsPublic) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); + } + + try + { + cache.CallValueConstructor(ref data); + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + internal void InvalidateCachedNestedType() => Cache.InvalidateCachedNestedType(); internal bool IsGenericCOMObjectImpl() => RuntimeTypeHandle.IsComObject(this, true); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs index 710b1b507e4ef..e70078403f89c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs @@ -12,8 +12,12 @@ internal sealed partial class RuntimeType /// /// A cache which allows optimizing . /// - private sealed unsafe partial class CreateUninitializedCache + internal sealed unsafe partial class CreateUninitializedCache : IGenericCacheEntry { + public static CreateUninitializedCache Create(RuntimeType type) => new(type); + public void InitializeCompositeCache(RuntimeType.CompositeCacheEntry compositeEntry) => compositeEntry._createUninitializedCache = this; + public static ref CreateUninitializedCache? GetStorageRef(RuntimeType.CompositeCacheEntry compositeEntry) => ref compositeEntry._createUninitializedCache; + // The managed calli to the newobj allocator, plus its first argument (MethodTable*). private readonly delegate* _pfnAllocator; private readonly void* _allocatorFirstArg; @@ -22,7 +26,7 @@ private sealed unsafe partial class CreateUninitializedCache private readonly RuntimeType _originalRuntimeType; #endif - internal CreateUninitializedCache(RuntimeType rt) + private CreateUninitializedCache(RuntimeType rt) { Debug.Assert(rt != null); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.GenericCache.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.GenericCache.cs new file mode 100644 index 0000000000000..d59e096828c75 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.GenericCache.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using static System.RuntimeType; + +namespace System +{ + internal sealed partial class RuntimeType + { + /// + /// A composite cache entry that can store multiple cache entries of different kinds. + /// + internal sealed class CompositeCacheEntry : IGenericCacheEntry + { + internal ActivatorCache? _activatorCache; + internal CreateUninitializedCache? _createUninitializedCache; + internal RuntimeTypeCache.FunctionPointerCache? _functionPointerCache; + internal Array.ArrayInitializeCache? _arrayInitializeCache; + internal IGenericCacheEntry? _enumInfo; + internal BoxCache? _boxCache; + + void IGenericCacheEntry.InitializeCompositeCache(CompositeCacheEntry compositeEntry) => throw new UnreachableException(); + } + + /// + /// A base interface for all cache entries that can be stored in . + /// + internal interface IGenericCacheEntry + { + public void InitializeCompositeCache(CompositeCacheEntry compositeEntry); + } + + /// + /// A typed cache entry. This type provides a base type that handles contruction of entries and maintenance of + /// the in a . + /// + /// The cache entry type. + internal interface IGenericCacheEntry : IGenericCacheEntry + where TCache : class, IGenericCacheEntry + { + public static abstract TCache Create(RuntimeType type); + + public static abstract ref TCache? GetStorageRef(CompositeCacheEntry compositeEntry); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TCache GetOrCreate(RuntimeType type) + { + ref IGenericCacheEntry? genericCache = ref type.Cache.GenericCache; + // Read the GenericCache once to avoid multiple reads of the same field. + IGenericCacheEntry? currentCache = genericCache; + if (currentCache is not null) + { + if (currentCache is TCache existing) + { + return existing; + } + if (currentCache is CompositeCacheEntry composite) + { + TCache? existingComposite = TCache.GetStorageRef(composite); + if (existingComposite != null) + return existingComposite; + } + } + + return CreateAndCache(type); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TCache? Find(RuntimeType type) + { + ref IGenericCacheEntry? genericCache = ref type.Cache.GenericCache; + // Read the GenericCache once to avoid multiple reads of the same field. + IGenericCacheEntry? currentCache = genericCache; + if (currentCache is not null) + { + if (currentCache is TCache existing) + { + return existing; + } + if (currentCache is CompositeCacheEntry composite) + { + return TCache.GetStorageRef(composite); + } + } + + return null; + } + + public static TCache Replace(RuntimeType type, TCache newEntry) + { + ref IGenericCacheEntry? genericCache = ref type.Cache.GenericCache; + + // If the existing cache is of the same type, we can replace it directly, + // as long as it is not upgraded to a composite cache simultaneously. + while (true) + { + IGenericCacheEntry? existing = genericCache; + if (existing is not (null or TCache)) + break; // We lost the race and we can no longer replace the cache directly. + + if (Interlocked.CompareExchange(ref genericCache, newEntry, existing) == existing) + return newEntry; + // We lost the race, try again. + } + + // If we get here, either we have a composite cache or we need to upgrade to a composite cache. + while (true) + { + IGenericCacheEntry existing = genericCache!; + if (existing is not CompositeCacheEntry compositeCache) + { + compositeCache = new CompositeCacheEntry(); + existing.InitializeCompositeCache(compositeCache); + if (Interlocked.CompareExchange(ref genericCache, compositeCache, existing) != existing) + continue; // We lost the race, try again. + } + + TCache? existingEntry = TCache.GetStorageRef(compositeCache); + if (Interlocked.CompareExchange(ref TCache.GetStorageRef(compositeCache), newEntry, existingEntry) == existingEntry) + return newEntry; + // We lost the race, try again. + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static TCache CreateAndCache(RuntimeType type) + { + while (true) + { + ref IGenericCacheEntry? genericCache = ref type.Cache.GenericCache; + + // Update to CompositeCacheEntry if necessary + IGenericCacheEntry? existing = genericCache; + if (existing is null) + { + TCache newEntry = TCache.Create(type); + if (Interlocked.CompareExchange(ref genericCache, newEntry, null) == null) + return newEntry; + // We lost the race, try again. + } + else + { + if (existing is TCache existingTyped) + return existingTyped; + + if (existing is not CompositeCacheEntry compositeCache) + { + compositeCache = new CompositeCacheEntry(); + existing.InitializeCompositeCache(compositeCache); + if (Interlocked.CompareExchange(ref genericCache, compositeCache, existing) != existing) + continue; // We lost the race, try again. + } + + TCache newEntry = TCache.Create(type); + if (Interlocked.CompareExchange(ref TCache.GetStorageRef(compositeCache), newEntry, null) == null) + return newEntry; + // We lost the race, try again. + } + } + } + } + } +} diff --git a/src/coreclr/binder/assemblyname.cpp b/src/coreclr/binder/assemblyname.cpp index 0c96f6be47ec2..245c72c8d6a1f 100644 --- a/src/coreclr/binder/assemblyname.cpp +++ b/src/coreclr/binder/assemblyname.cpp @@ -25,7 +25,7 @@ namespace { - // See https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names#specifying-assembly-names + // See https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names#specifying-assembly-names const WCHAR* s_neutralCulture = W("neutral"); } diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 72c27bd38ee42..32a372d373862 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -319,7 +319,14 @@ CrashInfo::InitializeDAC(DumpType dumpType) m_dacModule = dlopen(dacPath.c_str(), RTLD_LAZY); if (m_dacModule == nullptr) { - printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror()); + if (m_appModel == AppModelType::SingleFile) + { + printf_error("Only full dumps are supported by single file apps. Change the dump type to full (DOTNET_DbgMiniDumpType=4)\n"); + } + else + { + printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror()); + } goto exit; } pfnDllMain = (PFN_DLLMAIN)dlsym(m_dacModule, "DllMain"); diff --git a/src/coreclr/debug/createdump/createdumpmain.cpp b/src/coreclr/debug/createdump/createdumpmain.cpp index e39538c4cb1c3..0d9dc69686538 100644 --- a/src/coreclr/debug/createdump/createdumpmain.cpp +++ b/src/coreclr/debug/createdump/createdumpmain.cpp @@ -229,6 +229,7 @@ int createdump_main(const int argc, const char* argv[]) exitCode = -1; } + fflush(stderr); fflush(g_stdout); if (g_logfile != nullptr) @@ -352,7 +353,7 @@ GetTimeStamp() #ifdef HOST_UNIX static void -trace_prefix() +trace_prefix(const char* format, va_list args) { // Only add this prefix if logging to the console if (g_logfile == nullptr) @@ -360,6 +361,8 @@ trace_prefix() fprintf(g_stdout, "[createdump] "); } fprintf(g_stdout, "%08" PRIx64 " ", GetTimeStamp()); + vfprintf(g_stdout, format, args); + fflush(g_stdout); } void @@ -369,9 +372,7 @@ trace_printf(const char* format, ...) { va_list args; va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); + trace_prefix(format, args); va_end(args); } } @@ -383,9 +384,7 @@ trace_verbose_printf(const char* format, ...) { va_list args; va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); + trace_prefix(format, args); va_end(args); } } @@ -397,9 +396,7 @@ CrashInfo::Trace(const char* format, ...) { va_list args; va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); + trace_prefix(format, args); va_end(args); } } @@ -411,9 +408,7 @@ CrashInfo::TraceVerbose(const char* format, ...) { va_list args; va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); + trace_prefix(format, args); va_end(args); } } diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 8b1771d7132e3..4b05e401a06b1 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1229,6 +1229,7 @@ class ClrDataAccess HRESULT Initialize(void); + HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData); HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data); BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord); diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 4657aadf22965..5353c93a89228 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -761,11 +761,52 @@ ClrDataAccess::GetHeapAllocData(unsigned int count, struct DacpGenerationAllocDa return hr; } -HRESULT -ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData) +HRESULT ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData* threadData) { SOSDacEnter(); + if (m_cdacSos != NULL) + { + hr = m_cdacSos->GetThreadData(threadAddr, threadData); + if (FAILED(hr)) + { + hr = GetThreadDataImpl(threadAddr, threadData); + } +#ifdef _DEBUG + else + { + DacpThreadData threadDataLocal; + HRESULT hrLocal = GetThreadDataImpl(threadAddr, &threadDataLocal); + _ASSERTE(hr == hrLocal); + _ASSERTE(threadData->corThreadId == threadDataLocal.corThreadId); + _ASSERTE(threadData->osThreadId == threadDataLocal.osThreadId); + _ASSERTE(threadData->state == threadDataLocal.state); + _ASSERTE(threadData->preemptiveGCDisabled == threadDataLocal.preemptiveGCDisabled); + _ASSERTE(threadData->allocContextPtr == threadDataLocal.allocContextPtr); + _ASSERTE(threadData->allocContextLimit == threadDataLocal.allocContextLimit); + _ASSERTE(threadData->context == threadDataLocal.context); + _ASSERTE(threadData->domain == threadDataLocal.domain); + _ASSERTE(threadData->pFrame == threadDataLocal.pFrame); + _ASSERTE(threadData->lockCount == threadDataLocal.lockCount); + _ASSERTE(threadData->firstNestedException == threadDataLocal.firstNestedException); + _ASSERTE(threadData->teb == threadDataLocal.teb); + _ASSERTE(threadData->fiberData == threadDataLocal.fiberData); + _ASSERTE(threadData->lastThrownObjectHandle == threadDataLocal.lastThrownObjectHandle); + _ASSERTE(threadData->nextThread == threadDataLocal.nextThread);; + } +#endif + } + else + { + hr = GetThreadDataImpl(threadAddr, threadData); + } + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData) +{ // marshal the Thread object from the target Thread* thread = PTR_Thread(TO_TADDR(threadAddr)); @@ -804,8 +845,7 @@ ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData * thread->m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo); #endif // FEATURE_EH_FUNCLETS - SOSDacLeave(); - return hr; + return S_OK; } #ifdef FEATURE_REJIT diff --git a/src/coreclr/debug/di/shimstackwalk.cpp b/src/coreclr/debug/di/shimstackwalk.cpp index 46213d4ca3649..a0d84f1a5ecb8 100644 --- a/src/coreclr/debug/di/shimstackwalk.cpp +++ b/src/coreclr/debug/di/shimstackwalk.cpp @@ -150,6 +150,14 @@ BOOL ShimStackWalk::ShouldTrackUMChain(StackWalkInfo * pswInfo) if (m_pProcess->IsThreadSuspendedOrHijacked(m_pThread)) return FALSE; + // In the case the thread is throwing a managed exception, + // TS_SyncSuspended might not yet be set, resulting in IsThreadSuspendedOrHijacked + // returning false above. We need to check the exception state to make sure we don't + // track the chain in this case. Since we know the type of Frame we are dealing with, + // we can make a more accurate determination of whether we should track the chain. + if (GetInternalFrameType(pswInfo->GetCurrentInternalFrame()) == STUBFRAME_EXCEPTION) + return FALSE; + return TRUE; } diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 2687ceff55353..e14d29f38ad79 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -105,14 +105,21 @@ CDAC_TYPES_BEGIN() CDAC_TYPE_BEGIN(Thread) CDAC_TYPE_INDETERMINATE(Thread) +CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_offsets::Id) +CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_offsets::OSId) CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets::ExposedObject) +CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_offsets::LastThrownObject) CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_offsets::Link) CDAC_TYPE_END(Thread) CDAC_TYPE_BEGIN(ThreadStore) CDAC_TYPE_INDETERMINATE(ThreadStore) -CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadCount, cdac_offsets::ThreadCount) -CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadList, cdac_offsets::ThreadList) +CDAC_TYPE_FIELD(ThreadStore, /*SLink*/, FirstThreadLink, cdac_offsets::FirstThreadLink) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, ThreadCount, cdac_offsets::ThreadCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, UnstartedCount, cdac_offsets::UnstartedCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, BackgroundCount, cdac_offsets::BackgroundCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_offsets::PendingCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_offsets::DeadCount) CDAC_TYPE_END(ThreadStore) CDAC_TYPE_BEGIN(GCHandle) @@ -123,6 +130,8 @@ CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) +CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread) +CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) #if FEATURE_EH_FUNCLETS CDAC_GLOBAL(FeatureEHFunclets, uint8, 1) #else diff --git a/src/coreclr/gc/env/gcenv.os.h b/src/coreclr/gc/env/gcenv.os.h index 01ed27dac3e59..75015b77262dd 100644 --- a/src/coreclr/gc/env/gcenv.os.h +++ b/src/coreclr/gc/env/gcenv.os.h @@ -422,7 +422,7 @@ class GCToOSInterface // Remarks: // If a process runs with a restricted memory limit, it returns the limit. If there's no limit // specified, it returns amount of actual physical memory. - static uint64_t GetPhysicalMemoryLimit(bool* is_restricted=NULL); + static uint64_t GetPhysicalMemoryLimit(bool* is_restricted=NULL, bool refresh=false); // Get memory status // Parameters: diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index ced393436f555..98e81eddd2c51 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -7318,10 +7318,14 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb #endif //USE_REGIONS dprintf(3, ("commit-accounting: commit in %d [%p, %p) for heap %d", bucket, address, ((uint8_t*)address + size), h_number)); - -#ifndef USE_REGIONS - if (bucket != recorded_committed_ignored_bucket) + bool should_count = +#ifdef USE_REGIONS + true; +#else + (bucket != recorded_committed_ignored_bucket); #endif //USE_REGIONS + + if (should_count) { check_commit_cs.Enter(); bool exceeded_p = false; @@ -7381,7 +7385,7 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb virtual_alloc_commit_for_heap (address, size, h_number)) : GCToOSInterface::VirtualCommit(address, size)); - if (!commit_succeeded_p && heap_hard_limit) + if (!commit_succeeded_p && should_count) { check_commit_cs.Enter(); committed_by_oh[bucket] -= size; @@ -18480,6 +18484,7 @@ bool gc_heap::should_retry_other_heap (int gen_number, size_t size) } } +#ifdef BACKGROUND_GC void gc_heap::bgc_record_uoh_allocation(int gen_number, size_t size) { assert((gen_number >= uoh_start_generation) && (gen_number < total_generation_count)); @@ -18502,6 +18507,7 @@ void gc_heap::bgc_record_uoh_allocation(int gen_number, size_t size) uoh_a_no_bgc[gen_number - uoh_start_generation] += size; } } +#endif //BACKGROUND_GC allocation_state gc_heap::allocate_uoh (int gen_number, size_t size, @@ -50043,6 +50049,7 @@ void gc_heap::check_and_adjust_bgc_tuning (int gen_number, size_t physical_size, } #endif //BGC_SERVO_TUNING +#ifdef BACKGROUND_GC void gc_heap::get_and_reset_uoh_alloc_info() { total_uoh_a_last_bgc = 0; @@ -50084,6 +50091,7 @@ void gc_heap::get_and_reset_uoh_alloc_info() total_uoh_a_last_bgc = total_uoh_a_no_bgc + total_uoh_a_bgc_marking + total_uoh_a_bgc_planning; } +#endif //BACKGROUND_GC bool gc_heap::is_pm_ratio_exceeded() { @@ -52832,7 +52840,7 @@ int gc_heap::refresh_memory_limit() size_t old_heap_hard_limit_poh = heap_hard_limit_oh[poh]; bool old_hard_limit_config_p = hard_limit_config_p; - total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&is_restricted_physical_mem); + total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&is_restricted_physical_mem, true); bool succeed = true; diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index da6c2f17c0774..6738d33a3247f 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -1764,7 +1764,9 @@ class gc_heap PER_HEAP_ISOLATED_METHOD void add_to_history(); +#ifdef BACKGROUND_GC PER_HEAP_ISOLATED_METHOD void get_and_reset_uoh_alloc_info(); +#endif //BACKGROUND_GC #ifdef BGC_SERVO_TUNING // Currently BGC servo tuning is an experimental feature. diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 686d67bfb509c..9219d8153a59d 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -1212,14 +1212,13 @@ size_t GCToOSInterface::GetVirtualMemoryMaxAddress() // Remarks: // If a process runs with a restricted memory limit, it returns the limit. If there's no limit // specified, it returns amount of actual physical memory. -uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) +uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted, bool refresh) { size_t restricted_limit; if (is_restricted) *is_restricted = false; - // The limit was not cached - if (g_RestrictedPhysicalMemoryLimit == 0) + if (g_RestrictedPhysicalMemoryLimit == 0 || refresh) { restricted_limit = GetRestrictedPhysicalMemoryLimit(); VolatileStore(&g_RestrictedPhysicalMemoryLimit, restricted_limit); diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp index 608751dd169af..4d7cfd40381f9 100644 --- a/src/coreclr/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/gc/windows/gcenv.windows.cpp @@ -969,7 +969,7 @@ size_t GCToOSInterface::GetVirtualMemoryLimit() // Remarks: // If a process runs with a restricted memory limit, it returns the limit. If there's no limit // specified, it returns amount of actual physical memory. -uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) +uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted, bool refresh) { if (is_restricted) *is_restricted = false; diff --git a/src/coreclr/gcinfo/gcinfodumper.cpp b/src/coreclr/gcinfo/gcinfodumper.cpp index 8acb4ea2d6b4b..a71ebe0dffd23 100644 --- a/src/coreclr/gcinfo/gcinfodumper.cpp +++ b/src/coreclr/gcinfo/gcinfodumper.cpp @@ -226,26 +226,26 @@ BOOL GcInfoDumper::ReportPointerRecord ( #undef REG #elif defined(TARGET_RISCV64) #undef REG -#define REG(reg, field) { offsetof(RiscV64VolatileContextPointer, field) } - REG(zero, R0), - REG(a0, A0), - REG(a1, A1), - REG(a2, A2), - REG(a3, A3), - REG(a4, A4), - REG(a5, A5), - REG(a6, A6), - REG(a7, A7), - REG(t0, T0), - REG(t1, T1), - REG(t2, T2), - REG(t3, T3), - REG(t4, T4), - REG(t5, T5), - REG(t6, T6), -#undef REG #define REG(reg, field) { offsetof(T_KNONVOLATILE_CONTEXT_POINTERS, field) } +#define vREG(reg, field) { offsetof(RiscV64VolatileContextPointer, field) } + vREG(zero, R0), + REG(Ra, Ra), + { offsetof(T_CONTEXT, Sp) }, + REG(Gp, Gp), + REG(Tp, Tp), + vREG(t0, T0), + vREG(t1, T1), + vREG(t2, T2), + REG(Fp, Fp), REG(s1, S1), + vREG(a0, A0), + vREG(a1, A1), + vREG(a2, A2), + vREG(a3, A3), + vREG(a4, A4), + vREG(a5, A5), + vREG(a6, A6), + vREG(a7, A7), REG(s2, S2), REG(s3, S3), REG(s4, S4), @@ -256,13 +256,12 @@ BOOL GcInfoDumper::ReportPointerRecord ( REG(s9, S9), REG(s10, S10), REG(s11, S11), - REG(ra, Ra), - REG(gp, Gp), - REG(tp, Tp), - REG(fp, Fp), - { offsetof(T_CONTEXT, Sp) }, + vREG(t3, T3), + vREG(t4, T4), + vREG(t5, T5), + vREG(t6, T6), +#undef vREG #undef REG - #else PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this platform.") #endif @@ -291,16 +290,11 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this iSPRegister = (offsetof(T_CONTEXT, Sp) - offsetof(T_CONTEXT, R0)) / sizeof(ULONGLONG); #endif -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) BYTE* pContext = (BYTE*)&(pRD->volatileCurrContextPointers); #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); BYTE* pContext = (BYTE*)pRD->pCurrentContext; -#elif defined(TARGET_RISCV64) - assert(!"unimplemented on RISCV64 yet"); - // TODO implement risc-v code, that should care about volatile registers (same as arm/arm64 architectures) - // instead of default code. - BYTE* pContext = (BYTE*)pRD->pCurrentContext; #else BYTE* pContext = (BYTE*)pRD->pCurrentContext; #endif @@ -370,7 +364,18 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); #elif defined(TARGET_RISCV64) - assert(!"unimplemented on RISCV64 yet"); + bool isVolatile = (iReg == 0 || (iReg >= 5 && iReg <= 7) || (iReg >= 10 && iReg <= 17) || iReg >= 28); + if (ctx == 0) + { + if (!isVolatile) + { + continue; + } + } + else if (isVolatile) // skip volatile registers for second context + { + continue; + } #endif { _ASSERTE(iReg < nCONTEXTRegisters); @@ -389,7 +394,7 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this pReg = *(SIZE_T**)((BYTE*)pRD->pCurrentContextPointers + rgRegisters[iEncodedReg].cbContextOffset); } -#elif defined(TARGET_ARM64) +#elif defined(TARGET_ARM64) || defined(TARGET_RISCV64) pReg = *(SIZE_T**)(pContext + rgRegisters[iReg].cbContextOffset); if (iEncodedReg == iSPRegister) { @@ -398,11 +403,6 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); pReg = (SIZE_T*)(pContext + rgRegisters[iReg].cbContextOffset); -#elif defined(TARGET_RISCV64) - assert(!"unimplemented on RISCV64 yet"); - // TODO implement risc-v code, that should care about volatile registers (same as arm/arm64 architectures) - // instead of default code. - pReg = (SIZE_T*)(pContext + rgRegisters[iReg].cbContextOffset); #else pReg = (SIZE_T*)(pContext + rgRegisters[iReg].cbContextOffset); #endif @@ -470,26 +470,18 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this GcStackSlotBase base; if (iSPRegister == iEncodedReg) { -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) base = GC_SP_REL; #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); //TODO: should confirm ? base = GC_SP_REL; -#elif defined(TARGET_RISCV64) - assert(!"unimplemented on RISCV64 yet"); - // TODO implement risc-v code, that should care about volatile registers (same as arm/arm64 architectures) - // instead of default code. - if (0 == ctx) - base = GC_SP_REL; - else - base = GC_CALLER_SP_REL; #else if (0 == ctx) base = GC_SP_REL; else base = GC_CALLER_SP_REL; -#endif //defined(TARGET_ARM) || defined(TARGET_ARM64) +#endif //defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) } else { @@ -511,15 +503,10 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this } } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) pContext = (BYTE*)pRD->pCurrentContextPointers; #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); -#elif defined(TARGET_RISCV64) - assert(!"unimplemented on RISCV64 yet"); - // TODO implement risc-v code, that should care about volatile registers (same as arm/arm64 architectures) - // instead of default code. - pContext = (BYTE*)pRD->pCallerContext; #else pContext = (BYTE*)pRD->pCallerContext; #endif @@ -846,14 +833,11 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 safePointOffset = offset; -#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) safePointOffset++; #elif defined(TARGET_LOONGARCH64) #pragma message("Unimplemented for LOONGARCH64 yet.") assert(!"unimplemented on LOONGARCH yet"); -#elif defined(TARGET_RISCV64) -#pragma message("Unimplemented for RISCV64 yet.") - assert(!"unimplemented on RISCV64 yet"); #endif if(safePointDecoder.IsSafePoint(safePointOffset)) { diff --git a/src/coreclr/ilasm/assembler.cpp b/src/coreclr/ilasm/assembler.cpp index e105c00162c55..4aa33378c0838 100644 --- a/src/coreclr/ilasm/assembler.cpp +++ b/src/coreclr/ilasm/assembler.cpp @@ -1420,6 +1420,15 @@ void Assembler::EmitOpcode(Instr* instr) pLPC->PC = m_CurPC; pLPC->pOwnerDocument = instr->pOwnerDocument; + if(m_pCurMethod->m_FirstDocument == NULL) + { + m_pCurMethod->m_FirstDocument = instr->pOwnerDocument; + } + else if (instr->pOwnerDocument != NULL && m_pCurMethod->m_FirstDocument != instr->pOwnerDocument) + { + m_pCurMethod->m_HasMultipleDocuments = TRUE; + } + if (0xfeefee == instr->linenum && 0xfeefee == instr->linenum_end && 0 == instr->column && diff --git a/src/coreclr/ilasm/method.cpp b/src/coreclr/ilasm/method.cpp index c8946e7f80d78..7fb5767b3bd2f 100644 --- a/src/coreclr/ilasm/method.cpp +++ b/src/coreclr/ilasm/method.cpp @@ -35,6 +35,8 @@ Method::Method(Assembler *pAssembler, Class *pClass, _In_ __nullterminated char m_pbsBody = NULL; m_fNewBody = TRUE; m_fNew = TRUE; + m_FirstDocument = NULL; + m_HasMultipleDocuments = FALSE; // move the PInvoke descriptor (if any) from Assembler // (Assembler gets the descriptor BEFORE it calls new Method) diff --git a/src/coreclr/ilasm/method.hpp b/src/coreclr/ilasm/method.hpp index 4af091a0c5dcd..8a0a1fd87293b 100644 --- a/src/coreclr/ilasm/method.hpp +++ b/src/coreclr/ilasm/method.hpp @@ -337,6 +337,8 @@ class Method unsigned m_LineNum; // debug info LinePCList m_LinePCList; + Document* m_FirstDocument; + BOOL m_HasMultipleDocuments; // custom values CustomDescrList m_CustomDescrList; // token relocs (used for OBJ generation only) diff --git a/src/coreclr/ilasm/portable_pdb.cpp b/src/coreclr/ilasm/portable_pdb.cpp index 5e56e55eb716e..68bd1bd72b950 100644 --- a/src/coreclr/ilasm/portable_pdb.cpp +++ b/src/coreclr/ilasm/portable_pdb.cpp @@ -209,6 +209,7 @@ HRESULT PortablePdbWriter::DefineSequencePoints(Method* method) ULONG localSigRid = 0; ULONG offset = 0; ULONG deltaLines = 0; + ULONG currDocumentRid = 0; LONG deltaColumns = 0; LONG deltaStartLine = 0; LONG deltaStartColumn = 0; @@ -225,7 +226,11 @@ HRESULT PortablePdbWriter::DefineSequencePoints(Method* method) // LocalSignature localSigRid = RidFromToken(method->m_LocalsSig); CompressUnsignedLong(localSigRid, blob); - // InitialDocument TODO: skip this for now + currDocumentRid = RidFromToken(method->m_FirstDocument->GetToken()); + if(method->m_HasMultipleDocuments) + { + CompressUnsignedLong(currDocumentRid, blob); + } } // SequencePointRecord :: = sequence-point-record | hidden-sequence-point-record @@ -250,6 +255,15 @@ HRESULT PortablePdbWriter::DefineSequencePoints(Method* method) if (!currSeqPoint->IsHidden) { + //document + if(RidFromToken(currSeqPoint->pOwnerDocument->GetToken()) != currDocumentRid) + { + //document-record + currDocumentRid = RidFromToken(currSeqPoint->pOwnerDocument->GetToken()); + CompressUnsignedLong(0, blob); + CompressUnsignedLong(currDocumentRid, blob); + } + //offset offset = (i == 0) ? currSeqPoint->PC : currSeqPoint->PC - prevSeqPoint->PC; CompressUnsignedLong(offset, blob); @@ -311,8 +325,8 @@ HRESULT PortablePdbWriter::DefineSequencePoints(Method* method) // finally define sequence points for the method if ((isValid && currSeqPoint != NULL) || hasEmptyMethodBody) { - mdDocument document = hasEmptyMethodBody ? m_currentDocument->GetToken() : currSeqPoint->pOwnerDocument->GetToken(); - ULONG documentRid = RidFromToken(document); + mdDocument document = hasEmptyMethodBody ? m_currentDocument->GetToken() : method->m_FirstDocument->GetToken(); + ULONG documentRid = method->m_HasMultipleDocuments ? 0 : RidFromToken(document); hr = m_pdbEmitter->DefineSequencePoints(documentRid, blob->ptr(), blob->length()); } diff --git a/src/coreclr/inc/check.h b/src/coreclr/inc/check.h index 30ea0fdaf4d81..4e8a6fd42307a 100644 --- a/src/coreclr/inc/check.h +++ b/src/coreclr/inc/check.h @@ -8,7 +8,6 @@ // Assertion checking infrastructure // --------------------------------------------------------------------------- - #ifndef CHECK_H_ #define CHECK_H_ @@ -16,6 +15,18 @@ #include "daccess.h" #include "unreachable.h" +// Use the C++ detection idiom (https://isocpp.org/blog/2017/09/detection-idiom-a-stopgap-for-concepts-simon-brand) +template struct make_void { using type = void; }; +template using void_t = typename make_void::type; + +// Macros for creating type traits to check if a member exists +#define DEFINE_MEMBER_EXISTENCE_CHECK(Member) \ +template \ +struct has_##Member : std::false_type {}; \ +\ +template \ +struct has_##Member().Member)>> : std::true_type {}; + #ifdef _DEBUG #ifdef _MSC_VER @@ -282,19 +293,34 @@ do \ #if CHECK_INVARIANTS +DEFINE_MEMBER_EXISTENCE_CHECK(Invariant); +DEFINE_MEMBER_EXISTENCE_CHECK(InternalInvariant); + +template +typename std::enable_if::value, CHECK>::type CheckInvariantOnly(TYPENAME &obj) +{ + CHECK(obj.Invariant()); + CHECK_OK; +} + +template +typename std::enable_if::value, CHECK>::type CheckInvariantOnly(TYPENAME &obj) { CHECK_OK; } + +template +typename std::enable_if::value, CHECK>::type CheckInternalInvariantOnly(TYPENAME &obj) +{ + CHECK(obj.InternalInvariant()); + CHECK_OK; +} + +template +typename std::enable_if::value, CHECK>::type CheckInternalInvariantOnly(TYPENAME &obj) { CHECK_OK; } + template CHECK CheckInvariant(TYPENAME &obj) { -#if defined(_MSC_VER) || defined(__llvm__) - __if_exists(TYPENAME::Invariant) - { - CHECK(obj.Invariant()); - } - __if_exists(TYPENAME::InternalInvariant) - { - CHECK(obj.InternalInvariant()); - } -#endif + CheckInvariantOnly(obj); + CheckInternalInvariantOnly(obj); CHECK_OK; } @@ -331,8 +357,9 @@ enum IsNullOK }; #if CHECK_INVARIANTS +DEFINE_MEMBER_EXISTENCE_CHECK(Check); template -CHECK CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK) +typename std::enable_if::value, CHECK>::type CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK) { if (o == NULL) { @@ -340,29 +367,35 @@ CHECK CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK) } else { -#if defined(_MSC_VER) || defined(__llvm__) - __if_exists(TYPENAME::Check) - { - CHECK(o->Check()); - } -#endif + CHECK(o->Check()); } CHECK_OK; } template -CHECK CheckValue(TYPENAME &val) +typename std::enable_if::value, CHECK>::type CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK) { -#if defined(_MSC_VER) || defined(__llvm__) - __if_exists(TYPENAME::Check) + if (o == NULL) { - CHECK(val.Check()); + CHECK_MSG(ok, "Illegal null pointer"); } -#endif + CHECK_OK; +} + +template +typename std::enable_if::value, CHECK>::type CheckValue(TYPENAME &val) +{ + CHECK(val.Check()); CHECK(CheckInvariant(val)); + CHECK_OK; +} +template +typename std::enable_if::value, CHECK>::type CheckValue(TYPENAME &val) +{ + CHECK(CheckInvariant(val)); CHECK_OK; } #else // CHECK_INVARIANTS diff --git a/src/coreclr/inc/iterator.h b/src/coreclr/inc/iterator.h index b7bb142665c46..df50ca948eed0 100644 --- a/src/coreclr/inc/iterator.h +++ b/src/coreclr/inc/iterator.h @@ -201,9 +201,9 @@ class IndexerBasePrototype SCOUNT_T Subtract(const IndexerBasePrototype &i) const; CHECK DoCheck(SCOUNT_T delta) const; }; - }; +DEFINE_MEMBER_EXISTENCE_CHECK(m_revision); template class CheckedIteratorBase @@ -214,20 +214,21 @@ class CheckedIteratorBase int m_revision; #endif - CHECK CheckRevision() const + template + typename std::enable_if::value, CHECK>::type CheckRevision() const { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; -#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__llvm__)) - __if_exists(CONTAINER::m_revision) - { - CHECK_MSG(m_revision == m_container->m_revision, - "Use of Iterator after container has been modified"); - } +#if defined(_DEBUG) + CHECK_MSG(m_revision == m_container->m_revision, + "Use of Iterator after container has been modified"); #endif CHECK_OK; } + template + typename std::enable_if::value, CHECK>::type CheckRevision() const { CHECK_OK; } + CheckedIteratorBase() { LIMITED_METHOD_DAC_CONTRACT; @@ -238,30 +239,34 @@ class CheckedIteratorBase CheckedIteratorBase(const CONTAINER *container) { - LIMITED_METHOD_CONTRACT; #if defined(_DEBUG) m_container = container; -#if defined(_MSC_VER) || defined(__llvm__) - __if_exists(CONTAINER::m_revision) - { - m_revision = m_container->m_revision; - } + Init(container, has_m_revision()); #endif + } + + void Init(const CONTAINER *container, std::true_type) + { +#if defined(_DEBUG) + m_revision = container->m_revision; #endif } - void Resync(const CONTAINER *container) + void Init(const CONTAINER *container, std::false_type) { /* No-op */ } + + template + typename std::enable_if::value, void>::type Resync(const CONTAINER *container) { LIMITED_METHOD_DAC_CONTRACT; -#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__llvm__)) - __if_exists(CONTAINER::m_revision) - { - m_revision = m_container->m_revision; - } +#if defined(_DEBUG) + m_revision = m_container->m_revision; #endif } + template + typename std::enable_if::value, void>::type Resync(const CONTAINER *container) { /* No-op */ } + #if defined(_DEBUG) const CONTAINER *GetContainerDebug() const { diff --git a/src/coreclr/inc/slist.h b/src/coreclr/inc/slist.h index 805413fd3da34..e48a078adda5d 100644 --- a/src/coreclr/inc/slist.h +++ b/src/coreclr/inc/slist.h @@ -26,6 +26,8 @@ #ifndef _H_SLIST_ #define _H_SLIST_ +#include "cdacoffsets.h" + //------------------------------------------------------------------ // struct SLink, to use a singly linked list // have a data member m_Link of type SLink in your class @@ -118,6 +120,8 @@ class SList PTR_SLink m_pHead; PTR_SLink m_pTail; + template friend struct ::cdac_offsets; + // get the list node within the object static SLink* GetLink (T* pLink) { diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 53e4b2bb0fe97..23680253c70ae 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -166,6 +166,12 @@ typedef LPSTR LPUTF8; #define DEBUGARG(x) #endif +#if defined(FEATURE_READYTORUN) +#define R2RARG(x) , x +#else +#define R2RARG(x) +#endif + #ifndef sizeofmember // Returns the size of a class or struct member. #define sizeofmember(c,m) (sizeof(((c*)0)->m)) diff --git a/src/coreclr/interop/comwrappers.hpp b/src/coreclr/interop/comwrappers.hpp index ac120d2abb783..47bf008ac5012 100644 --- a/src/coreclr/interop/comwrappers.hpp +++ b/src/coreclr/interop/comwrappers.hpp @@ -231,7 +231,7 @@ class TrackerObjectManager }; // Class used to hold COM objects (i.e. IUnknown base class) -// This class mimics the semantics of ATL::CComPtr (https://docs.microsoft.com/cpp/atl/reference/ccomptr-class). +// This class mimics the semantics of ATL::CComPtr (https://learn.microsoft.com/cpp/atl/reference/ccomptr-class). template struct ComHolder { diff --git a/src/coreclr/interop/inc/interoplib.h b/src/coreclr/interop/inc/interoplib.h index 37237f5f7cab3..684283b7133bd 100644 --- a/src/coreclr/interop/inc/interoplib.h +++ b/src/coreclr/interop/inc/interoplib.h @@ -52,7 +52,7 @@ namespace InteropLib // The returned context memory is guaranteed to be initialized to zero. void* Context; - // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ + // See https://learn.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ // for details. bool FromTrackerRuntime; diff --git a/src/coreclr/interop/referencetrackertypes.hpp b/src/coreclr/interop/referencetrackertypes.hpp index 239503e544d93..ff4d8b84ac20c 100644 --- a/src/coreclr/interop/referencetrackertypes.hpp +++ b/src/coreclr/interop/referencetrackertypes.hpp @@ -6,7 +6,7 @@ #include -// Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ +// Documentation found at https://learn.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ // 64bd43f8-bfee-4ec4-b7eb-2935158dae21 const GUID IID_IReferenceTrackerTarget = { 0x64bd43f8, 0xbfee, 0x4ec4, { 0xb7, 0xeb, 0x29, 0x35, 0x15, 0x8d, 0xae, 0x21 } }; diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 6d72657fd97f6..7f41f0ae7ac64 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -3128,6 +3128,10 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, return false; } + // For several of the scenarios below we may skip the costing logic + // since we know that the operand is always containable and therefore + // is always cost effective to propagate. + switch (intrinsicId) { #if defined(TARGET_ARM64) @@ -3144,13 +3148,8 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, #endif // TARGET_XARCH { // We can optimize when the constant is zero, but only - // for non floating-point since +0.0 == -0.0 - - if (!vecCon->IsZero() || varTypeIsFloating(simdBaseType)) - { - return false; - } - break; + // for non floating-point since +0.0 == -0.0. + return vecCon->IsZero() && !varTypeIsFloating(simdBaseType); } #if defined(TARGET_ARM64) @@ -3160,12 +3159,7 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, { // We can optimize when the constant is zero due to a // specialized encoding for the instruction - - if (!vecCon->IsZero()) - { - return false; - } - break; + return vecCon->IsZero(); } case NI_AdvSimd_CompareGreaterThan: @@ -3178,28 +3172,16 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, // We can optimize when the constant is zero, but only // for signed types, due to a specialized encoding for // the instruction - - if (!vecCon->IsZero() || varTypeIsUnsigned(simdBaseType)) - { - return false; - } - break; + return vecCon->IsZero() && !varTypeIsUnsigned(simdBaseType); } #endif // TARGET_ARM64 #if defined(TARGET_XARCH) - case NI_SSE2_Insert: case NI_SSE41_Insert: - case NI_SSE41_X64_Insert: { // We can optimize for float when the constant is zero // due to a specialized encoding for the instruction - - if ((simdBaseType != TYP_FLOAT) || !vecCon->IsZero()) - { - return false; - } - break; + return (simdBaseType == TYP_FLOAT) && vecCon->IsZero(); } case NI_AVX512F_CompareEqualMask: @@ -3207,15 +3189,24 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, { // We can optimize when the constant is zero, but only // for non floating-point since +0.0 == -0.0 - - if (!vecCon->IsZero() || varTypeIsFloating(simdBaseType)) - { - return false; - } - break; + return vecCon->IsZero() && !varTypeIsFloating(simdBaseType); } #endif // TARGET_XARCH + case NI_Vector128_Shuffle: +#if defined(TARGET_XARCH) + case NI_Vector256_Shuffle: + case NI_Vector512_Shuffle: +#elif defined(TARGET_ARM64) + case NI_Vector64_Shuffle: +#endif + { + // The shuffle indices need to be constant so we can preserve + // the node as a hwintrinsic instead of rewriting as a user call. + assert(parent->GetOperandCount() == 2); + return parent->IsUserCall() && (dest == parent->Op(2)); + } + default: { break; diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis index cfbc6a181e974..485d0c5df5fff 100644 --- a/src/coreclr/jit/clrjit.natvis +++ b/src/coreclr/jit/clrjit.natvis @@ -8,8 +8,8 @@ The .NET Foundation licenses this file to you under the MIT license. diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 2262fe3d9e2de..5130c79983241 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -994,7 +994,10 @@ class CodeGen final : public CodeGenInterface class HWIntrinsicImmOpHelper final { public: - HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin, int immNum = 1); + HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin); + + HWIntrinsicImmOpHelper( + CodeGen* codeGen, regNumber immReg, int immLowerBound, int immUpperBound, GenTreeHWIntrinsic* intrin); void EmitBegin(); void EmitCaseEnd(); @@ -1040,6 +1043,7 @@ class CodeGen final : public CodeGenInterface regNumber nonConstImmReg; regNumber branchTargetReg; }; + #endif // TARGET_ARM64 #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index b2328b7df04bb..a85b2c6d0f941 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5067,7 +5067,7 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R(INS_sve_ptest, EA_SCALABLE, REG_P2, REG_P14, INS_OPTS_SCALABLE_B); // PTEST , .B // IF_SVE_DK_3A - theEmitter->emitIns_R_R_R(INS_sve_cntp, EA_8BYTE, REG_R29, REG_P0, REG_P15, + theEmitter->emitIns_R_R_R(INS_sve_cntp, EA_SCALABLE, REG_R29, REG_P0, REG_P15, INS_OPTS_SCALABLE_D); // CNTP , , . // IF_SVE_GE_4A @@ -6351,21 +6351,21 @@ void CodeGen::genArm64EmitterUnitTestsSve() INS_OPTS_SCALABLE_B); // UQSHRNT ., ., # // IF_SVE_DL_2A - theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R0, REG_P0, INS_OPTS_SCALABLE_B, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, 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, + theEmitter->emitIns_R_R(INS_sve_cntp, EA_SCALABLE, REG_R7, REG_P7, INS_OPTS_SCALABLE_D, INS_SCALABLE_OPTS_VL_4X); // CNTP , ., // IF_SVE_DM_2A diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index a2ac6832d8222..ebf6bd7fc9908 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -584,7 +584,7 @@ void CodeGenInterface::genUpdateLife(VARSET_VALARG_TP newLife) // inline regMaskTP CodeGenInterface::genGetRegMask(const LclVarDsc* varDsc) { - regMaskTP regMask = RBM_NONE; + regMaskTP regMask; assert(varDsc->lvIsInReg()); @@ -3491,8 +3491,12 @@ void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed) // regMaskTP CodeGen::genGetParameterHomingTempRegisterCandidates() { - return RBM_CALLEE_TRASH | intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn | - regSet.rsGetModifiedRegsMask(); + regMaskTP regs = RBM_CALLEE_TRASH | intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn | + regSet.rsGetModifiedRegsMask(); + // We may have reserved register that the backend needs to access stack + // locals. We cannot place state in that register. + regs &= ~regSet.rsMaskResvd; + return regs; } /***************************************************************************** @@ -4828,7 +4832,7 @@ void CodeGen::genFinalizeFrame() regMaskTP homingCandidates = genGetParameterHomingTempRegisterCandidates(); if (((homingCandidates & ~intRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLINT) == RBM_NONE) { - regMaskTP extraRegMask = RBM_ALLINT & ~homingCandidates; + regMaskTP extraRegMask = RBM_ALLINT & ~homingCandidates & ~regSet.rsMaskResvd; assert(extraRegMask != RBM_NONE); regNumber extraReg = genFirstRegNumFromMask(extraRegMask); JITDUMP("No temporary registers are available for integer parameter homing. Adding %s\n", getRegName(extraReg)); @@ -4837,7 +4841,7 @@ void CodeGen::genFinalizeFrame() if (((homingCandidates & ~floatRegState.rsCalleeRegArgMaskLiveIn) & RBM_ALLFLOAT) == RBM_NONE) { - regMaskTP extraRegMask = RBM_ALLFLOAT & ~homingCandidates; + regMaskTP extraRegMask = RBM_ALLFLOAT & ~homingCandidates & ~regSet.rsMaskResvd; assert(extraRegMask != RBM_NONE); regNumber extraReg = genFirstRegNumFromMask(extraRegMask); JITDUMP("No temporary registers are available for float parameter homing. Adding %s\n", getRegName(extraReg)); diff --git a/src/coreclr/jit/codegeninterface.h b/src/coreclr/jit/codegeninterface.h index a025285cbc091..608c72c22d48d 100644 --- a/src/coreclr/jit/codegeninterface.h +++ b/src/coreclr/jit/codegeninterface.h @@ -75,31 +75,31 @@ class CodeGenInterface } #if defined(TARGET_AMD64) - SingleTypeRegSet rbmAllFloat; - SingleTypeRegSet rbmFltCalleeTrash; + regMaskTP rbmAllFloat; + regMaskTP rbmFltCalleeTrash; - FORCEINLINE SingleTypeRegSet get_RBM_ALLFLOAT() const + FORCEINLINE regMaskTP get_RBM_ALLFLOAT() const { return this->rbmAllFloat; } - FORCEINLINE SingleTypeRegSet get_RBM_FLT_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_FLT_CALLEE_TRASH() const { return this->rbmFltCalleeTrash; } #endif // TARGET_AMD64 #if defined(TARGET_XARCH) - SingleTypeRegSet rbmAllMask; - SingleTypeRegSet rbmMskCalleeTrash; + regMaskTP rbmAllMask; + regMaskTP rbmMskCalleeTrash; // Call this function after the equivalent fields in Compiler have been initialized. void CopyRegisterInfo(); - FORCEINLINE SingleTypeRegSet get_RBM_ALLMASK() const + FORCEINLINE regMaskTP get_RBM_ALLMASK() const { return this->rbmAllMask; } - FORCEINLINE SingleTypeRegSet get_RBM_MSK_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_MSK_CALLEE_TRASH() const { return this->rbmMskCalleeTrash; } diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 64c2b84e87b43..757491b95034d 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -9912,7 +9912,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) // if we reported the frame pointer in the prolog. The Windows x64 unwinding ABI specifically // disallows this `lea` form: // - // See https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code + // See https://learn.microsoft.com/cpp/build/prolog-and-epilog?view=msvc-160#epilog-code // // "When a frame pointer is not used, the epilog must use add RSP,constant to deallocate the fixed part of the // stack. It may not use lea RSP,constant[RSP] instead. This restriction exists so the unwind code has fewer diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 1b5592a5b59e5..54be15ed7f20b 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -3485,12 +3485,12 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // Make sure we copy the register info and initialize the // trash regs after the underlying fields are initialized - const SingleTypeRegSet vtCalleeTrashRegs[TYP_COUNT]{ + const regMaskTP vtCalleeTrashRegs[TYP_COUNT]{ #define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, csr, ctr, tf) ctr, #include "typelist.h" #undef DEF_TP }; - memcpy(varTypeCalleeTrashRegs, vtCalleeTrashRegs, sizeof(SingleTypeRegSet) * TYP_COUNT); + memcpy(varTypeCalleeTrashRegs, vtCalleeTrashRegs, sizeof(regMaskTP) * TYP_COUNT); codeGen->CopyRegisterInfo(); #endif // TARGET_XARCH diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 428c817c751c7..9281f55471ec7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -72,22 +72,21 @@ inline var_types genActualType(T value); * Forward declarations */ -struct InfoHdr; // defined in GCInfo.h -struct escapeMapping_t; // defined in fgdiagnostic.cpp -class emitter; // defined in emit.h -struct ShadowParamVarInfo; // defined in GSChecks.cpp -struct InitVarDscInfo; // defined in registerargconvention.h -class FgStack; // defined in fgbasic.cpp -class Instrumentor; // defined in fgprofile.cpp -class SpanningTreeVisitor; // defined in fgprofile.cpp -class CSE_DataFlow; // defined in optcse.cpp -struct CSEdsc; // defined in optcse.h -class CSE_HeuristicCommon; // defined in optcse.h -class OptBoolsDsc; // defined in optimizer.cpp -struct RelopImplicationInfo; // defined in redundantbranchopts.cpp -struct JumpThreadInfo; // defined in redundantbranchopts.cpp -class ProfileSynthesis; // defined in profilesynthesis.h -class LoopLocalOccurrences; // defined in inductionvariableopts.cpp +struct InfoHdr; // defined in GCInfo.h +struct escapeMapping_t; // defined in fgdiagnostic.cpp +class emitter; // defined in emit.h +struct ShadowParamVarInfo; // defined in GSChecks.cpp +struct InitVarDscInfo; // defined in registerargconvention.h +class FgStack; // defined in fgbasic.cpp +class Instrumentor; // defined in fgprofile.cpp +class SpanningTreeVisitor; // defined in fgprofile.cpp +class CSE_DataFlow; // defined in optcse.cpp +struct CSEdsc; // defined in optcse.h +class CSE_HeuristicCommon; // defined in optcse.h +class OptBoolsDsc; // defined in optimizer.cpp +struct JumpThreadInfo; // defined in redundantbranchopts.cpp +class ProfileSynthesis; // defined in profilesynthesis.h +class LoopLocalOccurrences; // defined in inductionvariableopts.cpp #ifdef DEBUG struct IndentStack; #endif @@ -1016,9 +1015,10 @@ class LclVarDsc regMaskTP lvRegMask() const { - regMaskTP regMask = RBM_NONE; if (GetRegNum() != REG_STK) { + regMaskTP regMask; + if (varTypeUsesFloatReg(this)) { regMask = genRegMaskFloat(GetRegNum() ARM_ARG(TypeGet())); @@ -1033,8 +1033,12 @@ class LclVarDsc regMask = genRegMask(GetRegNum()); } + return regMask; + } + else + { + return RBM_NONE; } - return regMask; } //----------------------------------------------------------------------------- @@ -2187,6 +2191,8 @@ class FlowGraphNaturalLoop return m_exitEdges[index]; } + BasicBlock* GetPreheader() const; + unsigned GetDepth() const; bool ContainsBlock(BasicBlock* block); @@ -2495,6 +2501,30 @@ enum class NodeThreading LIR, // Nodes are in LIR form (after rationalization) }; +//------------------------------------------------------------------------ +// RelopImplicationInfo +// +// Describes information needed to check for and describe the +// inferences between two relops. +// +struct RelopImplicationInfo +{ + // Dominating relop, whose value may be determined by control flow + ValueNum domCmpNormVN = ValueNumStore::NoVN; + // Dominated relop, whose value we would like to determine + ValueNum treeNormVN = ValueNumStore::NoVN; + // Relationship between the two relops, if any + ValueNumStore::VN_RELATION_KIND vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same; + // Can we draw an inference? + bool canInfer = false; + // If canInfer and dominating relop is true, can we infer value of dominated relop? + bool canInferFromTrue = true; + // If canInfer and dominating relop is false, can we infer value of dominated relop? + bool canInferFromFalse = true; + // Reverse the sense of the inference + bool reverseSense = false; +}; + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -4531,7 +4561,8 @@ class Compiler bool tailCall, bool callvirt, CORINFO_RESOLVED_TOKEN* pContstrainedResolvedToken, - CORINFO_THIS_TRANSFORM constraintCallThisTransform, + CORINFO_THIS_TRANSFORM constraintCallThisTransform + R2RARG(CORINFO_CONST_LOOKUP* entryPoint), NamedIntrinsic* pIntrinsicName, bool* isSpecialIntrinsic = nullptr); GenTree* impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, @@ -4540,7 +4571,8 @@ class Compiler NamedIntrinsic intrinsicName, bool mustExpand); GenTree* impMathIntrinsic(CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig + R2RARG(CORINFO_CONST_LOOKUP* entryPoint), var_types callType, NamedIntrinsic intrinsicName, bool tailCall); @@ -4573,10 +4605,13 @@ class Compiler bool mustExpand); #ifdef FEATURE_HW_INTRINSICS + bool IsValidForShuffle(GenTreeVecCon* vecCon, unsigned simdSize, var_types simdBaseType) const; + GenTree* impHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig + R2RARG(CORINFO_CONST_LOOKUP* entryPoint), bool mustExpand); GenTree* impSimdAsHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, @@ -4600,7 +4635,8 @@ class Compiler GenTree* impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig + R2RARG(CORINFO_CONST_LOOKUP* entryPoint), CorInfoType simdBaseJitType, var_types retType, unsigned simdSize, @@ -6756,6 +6792,8 @@ class Compiler PhaseStatus fgMarkAddressExposedLocals(); void fgSequenceLocals(Statement* stmt); + bool fgExposeUnpropagatedLocals(bool propagatedAny, class LocalEqualsLocalAddrAssertions* assertions); + void fgExposeLocalsInBitVec(BitVec_ValArg_T bitVec); PhaseStatus PhysicalPromotion(); @@ -6784,8 +6822,8 @@ class Compiler public: bool fgIsBigOffset(size_t offset); - bool IsValidLclAddr(unsigned lclNum, unsigned offset); + bool IsPotentialGCSafePoint(GenTree* tree) const; private: bool fgNeedReturnSpillTemp(); @@ -7521,10 +7559,14 @@ class Compiler #endif PhaseStatus optInductionVariables(); - bool optWidenPrimaryIV(FlowGraphNaturalLoop* loop, - unsigned lclNum, - ScevAddRec* addRec, - LoopLocalOccurrences* loopLocals); + + bool optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, + FlowGraphNaturalLoop* loop, + LoopLocalOccurrences* loopLocals); + bool optWidenPrimaryIV(FlowGraphNaturalLoop* loop, + unsigned lclNum, + ScevAddRec* addRec, + LoopLocalOccurrences* loopLocals); bool optCanSinkWidenedIV(unsigned lclNum, FlowGraphNaturalLoop* loop); bool optIsIVWideningProfitable(unsigned lclNum, @@ -10201,6 +10243,7 @@ class Compiler STRESS_MODE(UNWIND) /* stress unwind info; e.g., create function fragments */ \ STRESS_MODE(OPT_REPEAT) /* stress JitOptRepeat */ \ STRESS_MODE(INITIAL_PARAM_REG) /* Stress initial register assigned to parameters */ \ + STRESS_MODE(DOWNWARDS_COUNTED_LOOPS) /* Make more loops downwards counted */ \ \ /* After COUNT_VARN, stress level 2 does all of these all the time */ \ \ @@ -11246,8 +11289,8 @@ class Compiler // // Users of these values need to define four accessor functions: // - // SingleTypeRegSet get_RBM_ALLFLOAT(); - // SingleTypeRegSet get_RBM_FLT_CALLEE_TRASH(); + // regMaskTP get_RBM_ALLFLOAT(); + // regMaskTP get_RBM_FLT_CALLEE_TRASH(); // unsigned get_CNT_CALLEE_TRASH_FLOAT(); // unsigned get_AVAILABLE_REG_COUNT(); // @@ -11256,16 +11299,16 @@ class Compiler // This was done to avoid polluting all `targetXXX.h` macro definitions with a compiler parameter, where only // TARGET_AMD64 requires one. // - SingleTypeRegSet rbmAllFloat; - SingleTypeRegSet rbmFltCalleeTrash; - unsigned cntCalleeTrashFloat; + regMaskTP rbmAllFloat; + regMaskTP rbmFltCalleeTrash; + unsigned cntCalleeTrashFloat; public: - FORCEINLINE SingleTypeRegSet get_RBM_ALLFLOAT() const + FORCEINLINE regMaskTP get_RBM_ALLFLOAT() const { return this->rbmAllFloat; } - FORCEINLINE SingleTypeRegSet get_RBM_FLT_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_FLT_CALLEE_TRASH() const { return this->rbmFltCalleeTrash; } @@ -11284,8 +11327,8 @@ class Compiler // // Users of these values need to define four accessor functions: // - // SingleTypeRegSet get_RBM_ALLMASK(); - // SingleTypeRegSet get_RBM_MSK_CALLEE_TRASH(); + // regMaskTP get_RBM_ALLMASK(); + // regMaskTP get_RBM_MSK_CALLEE_TRASH(); // unsigned get_CNT_CALLEE_TRASH_MASK(); // unsigned get_AVAILABLE_REG_COUNT(); // @@ -11294,17 +11337,17 @@ class Compiler // This was done to avoid polluting all `targetXXX.h` macro definitions with a compiler parameter, where only // TARGET_XARCH requires one. // - SingleTypeRegSet rbmAllMask; - SingleTypeRegSet rbmMskCalleeTrash; - unsigned cntCalleeTrashMask; - SingleTypeRegSet varTypeCalleeTrashRegs[TYP_COUNT]; + regMaskTP rbmAllMask; + regMaskTP rbmMskCalleeTrash; + unsigned cntCalleeTrashMask; + regMaskTP varTypeCalleeTrashRegs[TYP_COUNT]; public: - FORCEINLINE SingleTypeRegSet get_RBM_ALLMASK() const + FORCEINLINE regMaskTP get_RBM_ALLMASK() const { return this->rbmAllMask; } - FORCEINLINE SingleTypeRegSet get_RBM_MSK_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_MSK_CALLEE_TRASH() const { return this->rbmMskCalleeTrash; } diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 648e2acc65acd..4c5910d53daca 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -101,6 +101,11 @@ inline bool genExactlyOneBit(T value) inline regMaskTP genFindLowestBit(regMaskTP value) { +#ifdef HAS_MORE_THAN_64_REGISTERS + // If we ever need to use this method for predicate + // registers, then handle it. + assert(value.getHigh() == RBM_NONE); +#endif return regMaskTP(genFindLowestBit(value.getLow())); } @@ -111,19 +116,14 @@ inline regMaskTP genFindLowestBit(regMaskTP value) inline bool genMaxOneBit(regMaskTP value) { +#ifdef HAS_MORE_THAN_64_REGISTERS + // If we ever need to use this method for predicate + // registers, then handle it. + assert(value.getHigh() == RBM_NONE); +#endif return genMaxOneBit(value.getLow()); } -/***************************************************************************** - * - * Return true if the given value has exactly one bit set. - */ - -inline bool genExactlyOneBit(regMaskTP value) -{ - return genExactlyOneBit(value.getLow()); -} - /***************************************************************************** * * Given a value that has exactly one bit set, return the position of that @@ -169,7 +169,7 @@ inline unsigned uhi32(uint64_t value) inline unsigned genCountBits(regMaskTP mask) { - return BitOperations::PopCount(mask.getLow()); + return PopCount(mask); } /***************************************************************************** @@ -933,8 +933,40 @@ inline unsigned Compiler::funGetFuncIdx(BasicBlock* block) // Assumptions: // The mask contains one and only one register. -inline regNumber genRegNumFromMask(regMaskTP mask) +inline regNumber genRegNumFromMask(const SingleTypeRegSet& mask) +{ + assert(mask != RBM_NONE); // Must have one bit set, so can't have a mask of zero + + /* Convert the mask to a register number */ + + regNumber regNum = (regNumber)genLog2(mask); + + /* Make sure we got it right */ + assert(genSingleTypeRegMask(regNum) == mask); + + return regNum; +} + +//------------------------------------------------------------------------------ +// genRegNumFromMask : Maps a single register mask having gpr/float to a register number. +// If the mask can contain predicate register, use genRegNumFromMask(reg, type) +// +// Arguments: +// mask - the register mask +// +// Return Value: +// The number of the register contained in the mask. +// +// Assumptions: +// The mask contains one and only one register. + +inline regNumber genRegNumFromMask(const regMaskTP& mask) { +#ifdef HAS_MORE_THAN_64_REGISTERS + // This method is only used for gpr/float + assert(mask.getHigh() == RBM_NONE); +#endif + assert(mask.IsNonEmpty()); // Must have one bit set, so can't have a mask of zero /* Convert the mask to a register number */ @@ -942,7 +974,74 @@ inline regNumber genRegNumFromMask(regMaskTP mask) regNumber regNum = (regNumber)genLog2(mask.getLow()); /* Make sure we got it right */ - assert(genRegMask(regNum) == mask.getLow()); + assert(genRegMask(regNum).getLow() == mask.getLow()); + + return regNum; +} + +//------------------------------------------------------------------------------ +// genRegNumFromMask : Maps a single register mask to a register number. +// +// Arguments: +// mask - the register mask +// type - The +// +// Return Value: +// The number of the register contained in the mask. +// +// Assumptions: +// The mask contains one and only one register. + +inline regNumber genRegNumFromMask(SingleTypeRegSet mask, var_types type) +{ + regNumber regNum = genRegNumFromMask(mask); + +#ifdef HAS_MORE_THAN_64_REGISTERS + if (varTypeIsMask(type)) + { + regNum = (regNumber)(64 + regNum); + } +#endif + return regNum; +} + +//------------------------------------------------------------------------------ +// genFirstRegNumFromMask : Maps first bit set in the register mask to a register number. +// +// Arguments: +// mask - the register mask +// +// Return Value: +// The number of the first register contained in the mask. +// +// TODO: check if const regMaskTP& matter or should we pass by value +inline regNumber genFirstRegNumFromMask(const regMaskTP& mask) +{ + assert(mask.IsNonEmpty()); // Must have one bit set, so can't have a mask of zero + + /* Convert the mask to a register number */ + + regNumber regNum = (regNumber)BitScanForward(mask); + + return regNum; +} + +//------------------------------------------------------------------------------ +// genFirstRegNumFromMask : Maps first bit set in the register mask to a register number. +// +// Arguments: +// mask - the register mask +// +// Return Value: +// The number of the first register contained in the mask. +// +inline regNumber genFirstRegNumFromMask(SingleTypeRegSet mask) +{ + assert(mask != RBM_NONE); // Must have one bit set, so can't have a mask of zero + + /* Convert the mask to a register number */ + + regNumber regNum = (regNumber)BitOperations::BitScanForward(mask); return regNum; } @@ -966,28 +1065,31 @@ inline regNumber genFirstRegNumFromMaskAndToggle(regMaskTP& mask) regNumber regNum = (regNumber)BitScanForward(mask); - mask ^= genRegMask(regNum); + mask.RemoveRegNumFromMask(regNum); return regNum; } //------------------------------------------------------------------------------ -// genFirstRegNumFromMask : Maps first bit set in the register mask to a register number. -// +// genFirstRegNumFromMaskAndToggle : Maps first bit set in the register mask to a +// register number and also toggle the bit in the `mask`. // Arguments: // mask - the register mask // // Return Value: -// The number of the first register contained in the mask. +// The number of the first register contained in the mask and updates the `mask` to toggle +// the bit. // -inline regNumber genFirstRegNumFromMask(regMaskTP mask) +inline regNumber genFirstRegNumFromMaskAndToggle(SingleTypeRegSet& mask) { - assert(mask.IsNonEmpty()); // Must have one bit set, so can't have a mask of zero + assert(mask != RBM_NONE); // Must have one bit set, so can't have a mask of zero /* Convert the mask to a register number */ - regNumber regNum = (regNumber)BitScanForward(mask); + regNumber regNum = (regNumber)BitOperations::BitScanForward(mask); + + mask ^= genSingleTypeRegMask(regNum); return regNum; } @@ -3190,6 +3292,41 @@ inline bool Compiler::IsValidLclAddr(unsigned lclNum, unsigned offset) return (offset < UINT16_MAX) && (offset < lvaLclExactSize(lclNum)); } +//------------------------------------------------------------------------ +// IsPotentialGCSafePoint: Can the given tree be effectively a gc safe point? +// +// Arguments: +// tree - the tree to check +// +// Return Value: +// True if the tree can be a gc safe point +// +inline bool Compiler::IsPotentialGCSafePoint(GenTree* tree) const +{ + if (((tree->gtFlags & GTF_CALL) != 0)) + { + // if this is not a No-GC helper + if (!tree->IsCall() || !emitter::emitNoGChelper(tree->AsCall()->GetHelperNum())) + { + // assume that we have a safe point. + return true; + } + } + + // TYP_STRUCT-typed stores might be converted into calls (with gc safe points) in Lower. + // This is quite a conservative fix as it's hard to prove Lower won't do it at this point. + if (tree->OperIsLocalStore()) + { + return tree->TypeIs(TYP_STRUCT); + } + if (tree->OperIs(GT_STORE_BLK)) + { + return true; + } + + return false; +} + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/src/coreclr/jit/emitarm64sve.cpp b/src/coreclr/jit/emitarm64sve.cpp index 80f1d55e97fd2..1fee8e529b170 100644 --- a/src/coreclr/jit/emitarm64sve.cpp +++ b/src/coreclr/jit/emitarm64sve.cpp @@ -2057,6 +2057,7 @@ void emitter::emitInsSve_R_R(instruction ins, break; case INS_sve_cntp: + assert(isScalableVectorSize(size)); assert(insOptsScalableStandard(opt)); assert(insScalableOptsWithVectorLength(sopt)); // l assert(isGeneralRegister(reg1)); // ddddd @@ -3918,7 +3919,7 @@ void emitter::emitInsSve_R_R_R(instruction ins, break; case INS_sve_cntp: - assert(size == EA_8BYTE); + assert(isScalableVectorSize(size)); assert(isGeneralRegister(reg1)); // ddddd assert(isPredicateRegister(reg2)); // gggg assert(isPredicateRegister(reg3)); // NNNN @@ -4383,6 +4384,12 @@ void emitter::emitInsSve_R_R_R(instruction ins, case INS_sve_ld1w: case INS_sve_ld1sw: case INS_sve_ld1d: + case INS_sve_ldnf1b: + case INS_sve_ldnf1sb: + case INS_sve_ldnf1h: + case INS_sve_ldnf1sh: + case INS_sve_ldnf1w: + case INS_sve_ldnf1sw: return emitIns_R_R_R_I(ins, size, reg1, reg2, reg3, 0, opt); default: @@ -13077,7 +13084,7 @@ void emitter::emitInsSveSanityCheck(instrDesc* id) break; case IF_SVE_DK_3A: // ........xx...... ..gggg.NNNNddddd -- SVE predicate count - assert(id->idOpSize() == EA_8BYTE); + assert(isScalableVectorSize(id->idOpSize())); assert(insOptsScalableStandard(id->idInsOpt())); assert(isGeneralRegister(id->idReg1())); // ddddd assert(isPredicateRegister(id->idReg2())); // gggg @@ -13338,9 +13345,13 @@ void emitter::emitInsSveSanityCheck(instrDesc* id) break; case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) - assert(id->idOpSize() == EA_8BYTE); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + assert(isGeneralRegister(id->idReg1())); // ddddd + assert(isPredicateRegister(id->idReg2())); // NNNN + assert(isScalableVectorSize(id->idOpSize())); + break; - 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())); @@ -15216,8 +15227,8 @@ void emitter::emitDispInsSveHelp(instrDesc* id) break; // , , . - case IF_SVE_DK_3A: // ........xx...... ..gggg.NNNNddddd -- SVE predicate count - emitDispReg(id->idReg1(), size, true); // ddddd + case IF_SVE_DK_3A: // ........xx...... ..gggg.NNNNddddd -- SVE predicate count + emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt, 2), id->idInsOpt(), true); // gggg emitDispPredicateReg(id->idReg3(), insGetPredicateType(fmt, 3), id->idInsOpt(), false); // NNNN break; @@ -15238,7 +15249,7 @@ void emitter::emitDispInsSveHelp(instrDesc* id) // , ., case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) - emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd + emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // NNNN emitDispVectorLengthSpecifier(id); break; diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index f1451dc83ef2c..e72777a296e61 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -3347,15 +3347,29 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) return sz; } -bool emitter::emitDispBranchInstrType(unsigned opcode2) const +/*****************************************************************************/ +/*****************************************************************************/ + +// clang-format off +static const char* const RegNames[] = { + #define REGDEF(name, rnum, mask, sname) sname, + #include "register.h" +}; +// clang-format on + +bool emitter::emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& print_second_reg) const +{ + print_second_reg = true; switch (opcode2) { case 0: - printf("beq "); + printf(is_zero_reg ? "beqz" : "beq "); + print_second_reg = !is_zero_reg; break; case 1: - printf("bne "); + printf(is_zero_reg ? "bnez" : "bne "); + print_second_reg = !is_zero_reg; break; case 4: printf("blt "); @@ -3407,17 +3421,19 @@ void emitter::emitDispBranchLabel(const instrDesc* id) const printf("L_M%03u_", FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum); } -bool emitter::emitDispBranch(unsigned opcode2, - const char* register1Name, - const char* register2Name, - const instrDesc* id, - const insGroup* ig) const +bool emitter::emitDispBranch( + unsigned opcode2, unsigned rs1, unsigned rs2, const instrDesc* id, const insGroup* ig) const { - if (!emitDispBranchInstrType(opcode2)) + bool print_second_reg = true; + if (!emitDispBranchInstrType(opcode2, rs2 == REG_ZERO, print_second_reg)) { return false; } - printf(" %s, %s, ", register1Name, register2Name); + printf(" %s, ", RegNames[rs1]); + if (print_second_reg) + { + printf("%s, ", RegNames[rs2]); + } assert(id != nullptr); if (id->idAddr()->iiaHasInstrCount()) { @@ -3438,17 +3454,6 @@ void emitter::emitDispIllegalInstruction(code_t instructionCode) printf("RISCV64 illegal instruction: 0x%08X\n", instructionCode); } -/*****************************************************************************/ -/*****************************************************************************/ - -// clang-format off -static const char* const RegNames[] = -{ - #define REGDEF(name, rnum, mask, sname) sname, - #include "register.h" -}; -// clang-format on - //---------------------------------------------------------------------------------------- // Disassemble the given instruction. // The `emitter::emitDispInsName` is focused on the most important for debugging. @@ -3470,6 +3475,8 @@ static const char* const RegNames[] = void emitter::emitDispInsName( code_t code, const BYTE* addr, bool doffs, unsigned insOffset, const instrDesc* id, const insGroup* ig) { + static constexpr int kMaxInstructionLength = 14; + const BYTE* insAdr = addr - writeableOffset; unsigned int opcode = code & 0x7f; @@ -3506,51 +3513,95 @@ void emitter::emitDispInsName( } case 0x13: { - unsigned int opcode2 = (code >> 12) & 0x7; - const char* rd = RegNames[(code >> 7) & 0x1f]; - const char* rs1 = RegNames[(code >> 15) & 0x1f]; - int imm12 = (((int)code) >> 20); // & 0xfff; - // if (imm12 & 0x800) - //{ - // imm12 |= 0xfffff000; - //} + unsigned opcode2 = (code >> 12) & 0x7; + unsigned rd = (code >> 7) & 0x1f; + unsigned rs1 = (code >> 15) & 0x1f; + int imm12 = static_cast(code) >> 20; + bool isHex = false; + bool hasImmediate = true; + int printLength = 0; + switch (opcode2) { - case 0x0: // ADDI - printf("addi %s, %s, %d\n", rd, rs1, imm12); - return; - case 0x1: // SLLI - printf("slli %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6 BITS for SHAMT in RISCV64 - return; + case 0x0: // ADDI & MV & NOP + if (code == emitInsCode(INS_nop)) + { + printf("nop\n"); + return; + } + else if (imm12 != 0) + { + printLength = printf("addi"); + } + else + { + printLength = printf("mv"); + hasImmediate = false; + } + break; + case 0x1: // SLLI + { + static constexpr unsigned kSlliFunct6 = 0b000000; + + unsigned funct6 = (imm12 >> 6) & 0x3f; + // SLLI's instruction code's upper 6 bits have to be equal to zero + if (funct6 != kSlliFunct6) + { + return emitDispIllegalInstruction(code); + } + printLength = printf("slli"); + imm12 &= 0x3f; // 6 BITS for SHAMT in RISCV64 + } + break; case 0x2: // SLTI - printf("slti %s, %s, %d\n", rd, rs1, imm12); - return; + printLength = printf("slti"); + break; case 0x3: // SLTIU - printf("sltiu %s, %s, %d\n", rd, rs1, imm12); - return; + printLength = printf("sltiu"); + break; case 0x4: // XORI - printf("xori %s, %s, 0x%x\n", rd, rs1, imm12); - return; + printLength = printf("xori"); + isHex = true; + break; case 0x5: // SRLI & SRAI - if (((code >> 30) & 0x1) == 0) - { - printf("srli %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6BITS for SHAMT in RISCV64 - } - else + { + static constexpr unsigned kLogicalShiftFunct6 = 0b000000; + static constexpr unsigned kArithmeticShiftFunct6 = 0b010000; + + unsigned funct6 = (imm12 >> 6) & 0x3f; + bool isLogicalShift = funct6 == kLogicalShiftFunct6; + if ((!isLogicalShift) && (funct6 != kArithmeticShiftFunct6)) { - printf("srai %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6BITS for SHAMT in RISCV64 + return emitDispIllegalInstruction(code); } - return; + printLength = printf(isLogicalShift ? "srli" : "srai"); + imm12 &= 0x3f; // 6BITS for SHAMT in RISCV64 + } + break; case 0x6: // ORI - printf("ori %s, %s, 0x%x\n", rd, rs1, imm12 & 0xfff); - return; + printLength = printf("ori"); + imm12 &= 0xfff; + isHex = true; + break; case 0x7: // ANDI - printf("andi %s, %s, 0x%x\n", rd, rs1, imm12 & 0xfff); - return; + printLength = printf("andi"); + imm12 &= 0xfff; + isHex = true; + break; default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; + return emitDispIllegalInstruction(code); } + assert(printLength > 0); + int paddingLength = kMaxInstructionLength - printLength; + + printf("%*s %s, %s", paddingLength, "", RegNames[rd], RegNames[rs1]); + if (hasImmediate) + { + printf(isHex ? ", 0x%x" : ", %d", imm12); + } + printf("\n"); + + return; } case 0x1b: { @@ -3574,185 +3625,198 @@ void emitter::emitDispInsName( printf("addiw %s, %s, %d\n", rd, rs1, imm12); } return; - case 0x1: // SLLIW - printf("slliw %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6 BITS for SHAMT in RISCV64 + case 0x1: // SLLIW + { + static constexpr unsigned kSlliwFunct7 = 0b0000000; + + unsigned funct7 = (imm12 >> 5) & 0x7f; + // SLLIW's instruction code's upper 7 bits have to be equal to zero + if (funct7 == kSlliwFunct7) + { + printf("slliw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5 BITS for SHAMT in RISCV64 + } + else + { + emitDispIllegalInstruction(code); + } + } return; case 0x5: // SRLIW & SRAIW - if (((code >> 30) & 0x1) == 0) + { + static constexpr unsigned kLogicalShiftFunct7 = 0b0000000; + static constexpr unsigned kArithmeticShiftFunct7 = 0b0100000; + + unsigned funct7 = (imm12 >> 5) & 0x7f; + if (funct7 == kLogicalShiftFunct7) { printf("srliw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64 } - else + else if (funct7 == kArithmeticShiftFunct7) { printf("sraiw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64 } + else + { + emitDispIllegalInstruction(code); + } + } return; default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; + return emitDispIllegalInstruction(code); } } case 0x33: { - unsigned int opcode2 = (code >> 25) & 0x3; + unsigned int opcode2 = (code >> 25) & 0x7f; unsigned int opcode3 = (code >> 12) & 0x7; const char* rd = RegNames[(code >> 7) & 0x1f]; const char* rs1 = RegNames[(code >> 15) & 0x1f]; const char* rs2 = RegNames[(code >> 20) & 0x1f]; - if (opcode2 == 0) + + switch (opcode2) { - switch (opcode3) - { - case 0x0: // ADD & SUB - if (((code >> 30) & 0x1) == 0) - { + case 0b0000000: + switch (opcode3) + { + case 0x0: // ADD printf("add %s, %s, %s\n", rd, rs1, rs2); - } - else - { - printf("sub %s, %s, %s\n", rd, rs1, rs2); - } - return; - case 0x1: // SLL - printf("sll %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x2: // SLT - printf("slt %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x3: // SLTU - printf("sltu %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x4: // XOR - printf("xor %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x5: // SRL & SRA - if (((code >> 30) & 0x1) == 0) - { + return; + case 0x1: // SLL + printf("sll %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x2: // SLT + printf("slt %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x3: // SLTU + printf("sltu %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x4: // XOR + printf("xor %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // SRL printf("srl %s, %s, %s\n", rd, rs1, rs2); - } - else - { + return; + case 0x6: // OR + printf("or %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x7: // AND + printf("and %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } + return; + case 0b0100000: + switch (opcode3) + { + case 0x0: // SUB + printf("sub %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // SRA printf("sra %s, %s, %s\n", rd, rs1, rs2); - } - return; - case 0x6: // OR - printf("or %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x7: // AND - printf("and %s, %s, %s\n", rd, rs1, rs2); - return; - default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; - } - } - else if (opcode2 == 0x1) - { - switch (opcode3) - { - case 0x0: // MUL - printf("mul %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x1: // MULH - printf("mulh %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x2: // MULHSU - printf("mulhsu %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x3: // MULHU - printf("mulhu %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x4: // DIV - printf("div %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x5: // DIVU - printf("divu %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x6: // REM - printf("rem %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x7: // REMU - printf("remu %s, %s, %s\n", rd, rs1, rs2); - return; - default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; - } - } - else - { - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; + return; + default: + return emitDispIllegalInstruction(code); + } + return; + case 0b0000001: + switch (opcode3) + { + case 0x0: // MUL + printf("mul %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x1: // MULH + printf("mulh %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x2: // MULHSU + printf("mulhsu %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x3: // MULHU + printf("mulhu %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x4: // DIV + printf("div %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // DIVU + printf("divu %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x6: // REM + printf("rem %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x7: // REMU + printf("remu %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } + return; + default: + return emitDispIllegalInstruction(code); } } case 0x3b: { - unsigned int opcode2 = (code >> 25) & 0x3; + unsigned int opcode2 = (code >> 25) & 0x7f; unsigned int opcode3 = (code >> 12) & 0x7; const char* rd = RegNames[(code >> 7) & 0x1f]; const char* rs1 = RegNames[(code >> 15) & 0x1f]; const char* rs2 = RegNames[(code >> 20) & 0x1f]; - if (opcode2 == 0) + switch (opcode2) { - switch (opcode3) - { - case 0x0: // ADDW & SUBW - if (((code >> 30) & 0x1) == 0) - { + case 0b0000000: + switch (opcode3) + { + case 0x0: // ADDW printf("addw %s, %s, %s\n", rd, rs1, rs2); - } - else - { - printf("subw %s, %s, %s\n", rd, rs1, rs2); - } - return; - case 0x1: // SLLW - printf("sllw %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x5: // SRLW & SRAW - if (((code >> 30) & 0x1) == 0) - { + return; + case 0x1: // SLLW + printf("sllw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // SRLW printf("srlw %s, %s, %s\n", rd, rs1, rs2); - } - else - { + return; + default: + return emitDispIllegalInstruction(code); + } + return; + case 0b0100000: + switch (opcode3) + { + case 0x0: // SUBW + printf("subw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // SRAW printf("sraw %s, %s, %s\n", rd, rs1, rs2); - } - return; - default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; - } - } - else if (opcode2 == 1) - { - switch (opcode3) - { - case 0x0: // MULW - printf("mulw %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x4: // DIVW - printf("divw %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x5: // DIVUW - printf("divuw %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x6: // REMW - printf("remw %s, %s, %s\n", rd, rs1, rs2); - return; - case 0x7: // REMUW - printf("remuw %s, %s, %s\n", rd, rs1, rs2); - return; - default: - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; - } - } - else - { - printf("RISCV64 illegal instruction: 0x%08X\n", code); - return; + return; + default: + return emitDispIllegalInstruction(code); + } + return; + case 0b0000001: + switch (opcode3) + { + case 0x0: // MULW + printf("mulw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x4: // DIVW + printf("divw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x5: // DIVUW + printf("divuw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x6: // REMW + printf("remw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x7: // REMUW + printf("remuw %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } + return; + default: + return emitDispIllegalInstruction(code); } } case 0x23: @@ -3787,9 +3851,9 @@ void emitter::emitDispInsName( } case 0x63: // BRANCH { - unsigned int opcode2 = (code >> 12) & 0x7; - const char* rs1 = RegNames[(code >> 15) & 0x1f]; - const char* rs2 = RegNames[(code >> 20) & 0x1f]; + unsigned opcode2 = (code >> 12) & 0x7; + unsigned rs1 = (code >> 15) & 0x1f; + unsigned rs2 = (code >> 20) & 0x1f; // int offset = (((code >> 31) & 0x1) << 12) | (((code >> 7) & 0x1) << 11) | (((code >> 25) & 0x3f) << 5) | // (((code >> 8) & 0xf) << 1); // if (offset & 0x800) @@ -3866,14 +3930,21 @@ void emitter::emitDispInsName( } case 0x6f: { - const char* rd = RegNames[(code >> 7) & 0x1f]; - int offset = (((code >> 31) & 0x1) << 20) | (((code >> 12) & 0xff) << 12) | (((code >> 20) & 0x1) << 11) | + unsigned rd = (code >> 7) & 0x1f; + int offset = (((code >> 31) & 0x1) << 20) | (((code >> 12) & 0xff) << 12) | (((code >> 20) & 0x1) << 11) | (((code >> 21) & 0x3ff) << 1); if (offset & 0x80000) { offset |= 0xfff00000; } - printf("jal %s, %d", rd, offset); + if (rd == REG_ZERO) + { + printf("j %d", offset); + } + else + { + printf("jal %s, %d", RegNames[rd], offset); + } CORINFO_METHOD_HANDLE handle = (CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie; if (handle != 0) { diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 5409c76233de9..6c73f3fe577fc 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -63,14 +63,10 @@ bool emitInsIsLoadOrStore(instruction ins); void emitDispInsName( code_t code, const BYTE* addr, bool doffs, unsigned insOffset, const instrDesc* id, const insGroup* ig); void emitDispInsInstrNum(const instrDesc* id) const; -bool emitDispBranch(unsigned opcode2, - const char* register1Name, - const char* register2Name, - const instrDesc* id, - const insGroup* ig) const; +bool emitDispBranch(unsigned opcode2, unsigned rs1, unsigned rs2, const instrDesc* id, const insGroup* ig) const; void emitDispBranchOffset(const instrDesc* id, const insGroup* ig) const; void emitDispBranchLabel(const instrDesc* id) const; -bool emitDispBranchInstrType(unsigned opcode2) const; +bool emitDispBranchInstrType(unsigned opcode2, bool is_zero_reg, bool& print_second_reg) const; void emitDispIllegalInstruction(code_t instructionCode); emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/) const; diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 9895d5c940c04..56d8a1bca6c1f 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -1400,10 +1400,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed case NI_Vector3_Create: case NI_Vector3_CreateBroadcast: case NI_Vector3_CreateFromVector2: - case NI_Vector4_Create: - case NI_Vector4_CreateBroadcast: - case NI_Vector4_CreateFromVector2: - case NI_Vector4_CreateFromVector3: case NI_Vector128_Create: case NI_Vector128_CreateScalar: case NI_Vector128_CreateScalarUnsafe: @@ -1630,82 +1626,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed break; } -#if defined(FEATURE_HW_INTRINSICS) -#if defined(TARGET_ARM64) - case NI_Vector64_As: - case NI_Vector64_AsByte: - case NI_Vector64_AsDouble: - case NI_Vector64_AsInt16: - case NI_Vector64_AsInt32: - case NI_Vector64_AsInt64: - case NI_Vector64_AsNInt: - case NI_Vector64_AsNUInt: - case NI_Vector64_AsSByte: - case NI_Vector64_AsSingle: - case NI_Vector64_AsUInt16: - case NI_Vector64_AsUInt32: - case NI_Vector64_AsUInt64: - case NI_Vector64_op_UnaryPlus: -#endif // TARGET_XARCH - case NI_Vector128_As: - case NI_Vector128_AsByte: - case NI_Vector128_AsDouble: - case NI_Vector128_AsInt16: - case NI_Vector128_AsInt32: - case NI_Vector128_AsInt64: - case NI_Vector128_AsNInt: - case NI_Vector128_AsNUInt: - case NI_Vector128_AsSByte: - case NI_Vector128_AsSingle: - case NI_Vector128_AsUInt16: - case NI_Vector128_AsUInt32: - case NI_Vector128_AsUInt64: - case NI_Vector128_AsVector4: - case NI_Vector128_op_UnaryPlus: - case NI_VectorT_As: - case NI_VectorT_AsVectorByte: - case NI_VectorT_AsVectorDouble: - case NI_VectorT_AsVectorInt16: - case NI_VectorT_AsVectorInt32: - case NI_VectorT_AsVectorInt64: - case NI_VectorT_AsVectorNInt: - case NI_VectorT_AsVectorNUInt: - case NI_VectorT_AsVectorSByte: - case NI_VectorT_AsVectorSingle: - case NI_VectorT_AsVectorUInt16: - case NI_VectorT_AsVectorUInt32: - case NI_VectorT_AsVectorUInt64: - case NI_VectorT_op_UnaryPlus: -#if defined(TARGET_XARCH) - case NI_Vector256_As: - case NI_Vector256_AsByte: - case NI_Vector256_AsDouble: - case NI_Vector256_AsInt16: - case NI_Vector256_AsInt32: - case NI_Vector256_AsInt64: - case NI_Vector256_AsNInt: - case NI_Vector256_AsNUInt: - case NI_Vector256_AsSByte: - case NI_Vector256_AsSingle: - case NI_Vector256_AsUInt16: - case NI_Vector256_AsUInt32: - case NI_Vector256_AsUInt64: - case NI_Vector256_op_UnaryPlus: - case NI_Vector512_As: - case NI_Vector512_AsByte: - case NI_Vector512_AsDouble: - case NI_Vector512_AsInt16: - case NI_Vector512_AsInt32: - case NI_Vector512_AsInt64: - case NI_Vector512_AsNInt: - case NI_Vector512_AsNUInt: - case NI_Vector512_AsSByte: - case NI_Vector512_AsSingle: - case NI_Vector512_AsUInt16: - case NI_Vector512_AsUInt32: - case NI_Vector512_AsUInt64: -#endif // TARGET_XARCH -#endif // FEATURE_HW_INTRINSICS case NI_SRCS_UNSAFE_As: case NI_SRCS_UNSAFE_AsRef: case NI_SRCS_UNSAFE_BitCast: @@ -1724,27 +1644,16 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed #if defined(TARGET_ARM64) case NI_Vector64_get_AllBitsSet: case NI_Vector64_get_One: - case NI_Vector64_get_Zero: #endif // TARGET_ARM64 - case NI_Vector2_get_One: - case NI_Vector2_get_Zero: - case NI_Vector3_get_One: - case NI_Vector3_get_Zero: - case NI_Vector4_get_One: - case NI_Vector4_get_Zero: case NI_Vector128_get_AllBitsSet: case NI_Vector128_get_One: - case NI_Vector128_get_Zero: case NI_VectorT_get_AllBitsSet: case NI_VectorT_get_One: - case NI_VectorT_get_Zero: #if defined(TARGET_XARCH) case NI_Vector256_get_AllBitsSet: case NI_Vector256_get_One: - case NI_Vector256_get_Zero: case NI_Vector512_get_AllBitsSet: case NI_Vector512_get_One: - case NI_Vector512_get_Zero: #endif // TARGET_XARCH #endif // FEATURE_HW_INTRINSICS { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 47a8c243a2e9e..d05a815b12f71 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4141,6 +4141,28 @@ FlowGraphNaturalLoop::FlowGraphNaturalLoop(const FlowGraphDfsTree* dfsTree, Basi { } +//------------------------------------------------------------------------ +// GetPreheader: Get the preheader of this loop, if it has one. +// +// Returns: +// The preheader, or nullptr if there is no preheader. +// +BasicBlock* FlowGraphNaturalLoop::GetPreheader() const +{ + if (m_entryEdges.size() != 1) + { + return nullptr; + } + + BasicBlock* preheader = m_entryEdges[0]->getSourceBlock(); + if (!preheader->KindIs(BBJ_ALWAYS)) + { + return nullptr; + } + + return preheader; +} + //------------------------------------------------------------------------ // GetDepth: Get the depth of the loop. // diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 78a6aef4267e1..949d611519e4c 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -4128,6 +4128,8 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp) // first tree to be evaluated, and "lvl2" - the second. if (multiOp->IsReverseOp()) { + assert(!multiOp->AsHWIntrinsic()->IsUserCall()); + level = gtSetEvalOrder(multiOp->Op(2)); lvl2 = gtSetEvalOrder(multiOp->Op(1)); } @@ -4140,11 +4142,18 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp) // We want the more complex tree to be evaluated first. if (level < lvl2) { - bool canSwap = multiOp->IsReverseOp() ? gtCanSwapOrder(multiOp->Op(2), multiOp->Op(1)) - : gtCanSwapOrder(multiOp->Op(1), multiOp->Op(2)); + bool canSwap = false; + + if (!multiOp->AsHWIntrinsic()->IsUserCall()) + { + canSwap = multiOp->IsReverseOp() ? gtCanSwapOrder(multiOp->Op(2), multiOp->Op(1)) + : gtCanSwapOrder(multiOp->Op(1), multiOp->Op(2)); + } if (canSwap) { + assert(!multiOp->AsHWIntrinsic()->IsUserCall()); + if (multiOp->IsReverseOp()) { multiOp->ClearReverseOp(); @@ -6563,7 +6572,7 @@ bool GenTree::OperSupportsReverseOpEvalOrder(Compiler* comp) const #if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS) if (OperIsMultiOp()) { - return AsMultiOp()->GetOperandCount() == 2; + return (AsMultiOp()->GetOperandCount() == 2) && !AsMultiOp()->IsUserCall(); } #endif // FEATURE_SIMD || FEATURE_HW_INTRINSICS return false; @@ -7227,8 +7236,6 @@ ExceptionSetFlags GenTree::OperExceptions(Compiler* comp) if (hwIntrinsicNode->OperIsMemoryLoadOrStore()) { - assert((gtFlags & GTF_IND_NONFAULTING) == 0); - // TODO-CQ: We should use comp->fgAddrCouldBeNull on the address operand // to determine if this can actually produce an NRE or not @@ -9626,10 +9633,8 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) case GT_INTRINSIC: copy = new (this, GT_INTRINSIC) GenTreeIntrinsic(tree->TypeGet(), tree->AsOp()->gtOp1, tree->AsOp()->gtOp2, - tree->AsIntrinsic()->gtIntrinsicName, tree->AsIntrinsic()->gtMethodHandle); -#ifdef FEATURE_READYTORUN - copy->AsIntrinsic()->gtEntryPoint = tree->AsIntrinsic()->gtEntryPoint; -#endif + tree->AsIntrinsic()->gtIntrinsicName, + tree->AsIntrinsic()->gtMethodHandle R2RARG(tree->AsIntrinsic()->gtEntryPoint)); break; case GT_BOUNDS_CHECK: @@ -9711,6 +9716,12 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) tree->AsHWIntrinsic()->GetHWIntrinsicId(), tree->AsHWIntrinsic()->GetSimdBaseJitType(), tree->AsHWIntrinsic()->GetSimdSize()); copy->AsHWIntrinsic()->SetAuxiliaryJitType(tree->AsHWIntrinsic()->GetAuxiliaryJitType()); + + if (tree->AsHWIntrinsic()->IsUserCall()) + { + copy->AsHWIntrinsic()->SetMethodHandle(this, tree->AsHWIntrinsic()->GetMethodHandle() + R2RARG(tree->AsHWIntrinsic()->GetEntryPoint())); + } goto CLONE_MULTIOP_OPERANDS; #endif #if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS) @@ -18269,6 +18280,79 @@ unsigned GenTreeVecCon::ElementCount(unsigned simdSize, var_types simdBaseType) { return simdSize / genTypeSize(simdBaseType); } + +bool Compiler::IsValidForShuffle(GenTreeVecCon* vecCon, unsigned simdSize, var_types simdBaseType) const +{ +#if defined(TARGET_XARCH) + size_t elementSize = genTypeSize(simdBaseType); + size_t elementCount = simdSize / elementSize; + + if (simdSize == 32) + { + if (!compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + // While we could accelerate some functions on hardware with only AVX support + // it's likely not worth it overall given that IsHardwareAccelerated reports false + return false; + } + else if ((varTypeIsByte(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX512VBMI_VL)) || + (varTypeIsShort(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX512BW_VL))) + { + bool crossLane = false; + + for (size_t index = 0; index < elementCount; index++) + { + uint64_t value = vecCon->GetIntegralVectorConstElement(index, simdBaseType); + + if (value >= elementCount) + { + continue; + } + + if (index < (elementCount / 2)) + { + if (value >= (elementCount / 2)) + { + crossLane = true; + break; + } + } + else if (value < (elementCount / 2)) + { + crossLane = true; + break; + } + } + + if (crossLane) + { + // TODO-XARCH-CQ: We should emulate cross-lane shuffling for byte/sbyte and short/ushort + return false; + } + } + } + else if (simdSize == 64) + { + if (varTypeIsByte(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX512VBMI)) + { + // TYP_BYTE, TYP_UBYTE need AVX512VBMI. + return false; + } + } + else + { + assert(simdSize == 16); + + if (varTypeIsSmall(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_SSSE3)) + { + // TYP_BYTE, TYP_UBYTE, TYP_SHORT, and TYP_USHORT need SSSE3 to be able to shuffle any operation + return false; + } + } +#endif // TARGET_XARCH + + return true; +} #endif // FEATURE_HW_INTRINSICS*/ //------------------------------------------------------------------------ @@ -19570,6 +19654,49 @@ void GenTreeMultiOp::InitializeOperands(GenTree** operands, size_t operandCount) SetOperandCount(operandCount); } +//------------------------------------------------------------------------ +// GenTreeJitIntrinsic::SetMethodHandle: Sets the method handle for an intrinsic +// so that it can be rewritten back to a user call in a later phase +// +// Arguments: +// comp - The compiler instance +// methodHandle - The method handle representing the fallback handling for the intrinsic +// entryPoint - The entry point information required for R2R scenarios +// +// Notes: +// We need to ensure that the operands are not tracked inline so that we can track the +// underlying method handle. See the comment in GenTreeJitIntrinsic around why the union +// of fields exists. +// +void GenTreeJitIntrinsic::SetMethodHandle(Compiler* comp, + CORINFO_METHOD_HANDLE methodHandle R2RARG(CORINFO_CONST_LOOKUP entryPoint)) +{ + assert(OperIsHWIntrinsic() && !IsUserCall()); + gtFlags |= GTF_HW_USER_CALL; + + size_t operandCount = GetOperandCount(); + + if ((operandCount != 0) && (operandCount <= ArrLen(gtInlineOperands))) + { + GenTree** oldOperands = GetOperandArray(); + GenTree** newOperands = comp->getAllocator(CMK_ASTNode).allocate(operandCount); + + ResetOperandArray(operandCount, comp, newOperands, operandCount); + assert(GetOperandArray() == newOperands); + + for (size_t i = 0; i < operandCount; i++) + { + newOperands[i] = oldOperands[i]; + } + } + + gtMethodHandle = methodHandle; + +#if defined(FEATURE_READYTORUN) + gtEntryPoint = new (comp, CMK_ASTNode) CORINFO_CONST_LOOKUP(entryPoint); +#endif // FEATURE_READYTORUN +} + var_types GenTreeJitIntrinsic::GetAuxiliaryType() const { CorInfoType auxiliaryJitType = GetAuxiliaryJitType(); @@ -26361,7 +26488,7 @@ GenTreeFieldList* Compiler::gtConvertTableOpToFieldList(GenTree* op, unsigned fi LclVarDsc* opVarDsc = lvaGetDesc(op->AsLclVar()); unsigned lclNum = lvaGetLclNum(opVarDsc); unsigned fieldSize = opVarDsc->lvSize() / fieldCount; - var_types fieldType = TYP_SIMD16; + var_types fieldType = Compiler::getSIMDTypeForSize(fieldSize); GenTreeFieldList* fieldList = new (this, GT_FIELD_LIST) GenTreeFieldList(); int offset = 0; @@ -26594,6 +26721,9 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoad(GenTree** pAddr) const case NI_Sve_LoadVectorUInt16ZeroExtendToUInt64: case NI_Sve_LoadVectorUInt32ZeroExtendToInt64: case NI_Sve_LoadVectorUInt32ZeroExtendToUInt64: + case NI_Sve_Load2xVectorAndUnzip: + case NI_Sve_Load3xVectorAndUnzip: + case NI_Sve_Load4xVectorAndUnzip: addr = Op(2); break; #endif // TARGET_ARM64 @@ -26712,8 +26842,10 @@ bool GenTreeHWIntrinsic::OperIsMemoryStore(GenTree** pAddr) const case NI_Sve_StoreAndZipx3: case NI_Sve_StoreAndZipx4: case NI_Sve_StoreNarrowing: + case NI_Sve_StoreNonTemporal: addr = Op(2); break; + #endif // TARGET_ARM64 default: @@ -27037,7 +27169,7 @@ bool GenTreeHWIntrinsic::OperRequiresCallFlag() const } } - return false; + return IsUserCall(); } //------------------------------------------------------------------------------ @@ -27116,6 +27248,13 @@ ClassLayout* GenTreeHWIntrinsic::GetLayout(Compiler* compiler) const case NI_AdvSimd_Arm64_LoadAndReplicateToVector128x4: return compiler->typGetBlkLayout(64); + case NI_Sve_Load2xVectorAndUnzip: + return compiler->typGetBlkLayout(compiler->getVectorTByteLength() * 2); + case NI_Sve_Load3xVectorAndUnzip: + return compiler->typGetBlkLayout(compiler->getVectorTByteLength() * 3); + case NI_Sve_Load4xVectorAndUnzip: + return compiler->typGetBlkLayout(compiler->getVectorTByteLength() * 4); + #endif // TARGET_ARM64 default: diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 6a1b50e1569c9..578b2f43b349b 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -559,6 +559,7 @@ enum GenTreeFlags : unsigned int #ifdef FEATURE_HW_INTRINSICS GTF_HW_EM_OP = 0x10000000, // GT_HWINTRINSIC -- node is used as an operand to an embedded mask + GTF_HW_USER_CALL = 0x20000000, // GT_HWINTRINSIC -- node is implemented via a user call #endif // FEATURE_HW_INTRINSICS }; @@ -5951,24 +5952,30 @@ struct GenTreeIntrinsic : public GenTreeOp NamedIntrinsic gtIntrinsicName; CORINFO_METHOD_HANDLE gtMethodHandle; // Method handle of the method which is treated as an intrinsic. -#ifdef FEATURE_READYTORUN +#if defined(FEATURE_READYTORUN) // Call target lookup info for method call from a Ready To Run module CORINFO_CONST_LOOKUP gtEntryPoint; -#endif +#endif // FEATURE_READYTORUN - GenTreeIntrinsic(var_types type, GenTree* op1, NamedIntrinsic intrinsicName, CORINFO_METHOD_HANDLE methodHandle) + GenTreeIntrinsic(var_types type, + GenTree* op1, + NamedIntrinsic intrinsicName, + CORINFO_METHOD_HANDLE methodHandle R2RARG(CORINFO_CONST_LOOKUP entryPoint)) : GenTreeOp(GT_INTRINSIC, type, op1, nullptr) , gtIntrinsicName(intrinsicName) - , gtMethodHandle(methodHandle) + , gtMethodHandle(methodHandle) R2RARG(gtEntryPoint(entryPoint)) { assert(intrinsicName != NI_Illegal); } - GenTreeIntrinsic( - var_types type, GenTree* op1, GenTree* op2, NamedIntrinsic intrinsicName, CORINFO_METHOD_HANDLE methodHandle) + GenTreeIntrinsic(var_types type, + GenTree* op1, + GenTree* op2, + NamedIntrinsic intrinsicName, + CORINFO_METHOD_HANDLE methodHandle R2RARG(CORINFO_CONST_LOOKUP entryPoint)) : GenTreeOp(GT_INTRINSIC, type, op1, op2) , gtIntrinsicName(intrinsicName) - , gtMethodHandle(methodHandle) + , gtMethodHandle(methodHandle) R2RARG(gtEntryPoint(entryPoint)) { assert(intrinsicName != NI_Illegal); } @@ -6089,6 +6096,15 @@ struct GenTreeMultiOp : public GenTree } #endif + bool IsUserCall() const + { +#if defined(FEATURE_HW_INTRINSICS) + return OperIs(GT_HWINTRINSIC) && (gtFlags & GTF_HW_USER_CALL) != 0; +#else + return false; +#endif + } + GenTree*& Op(size_t index) { size_t actualIndex = index - 1; @@ -6217,7 +6233,29 @@ class IntrinsicNodeBuilder final struct GenTreeJitIntrinsic : public GenTreeMultiOp { protected: - GenTree* gtInlineOperands[2]; + union + { + // We don't have enough space to carry both the inline operands + // and the necessary information required to support rewriting + // the intrinsic back into a user call. As such, we union the + // data instead and use the GTF_HW_USER_CALL flag to indicate + // which fields are valid to access. -- Tracking the fields + // independently causes TREE_NODE_SZ_LARGE to increase and for + // GenTreeJitIntrinsic to become the largest node, which is + // undesirable, so this approach helps keep things pay-for-play. + + GenTree* gtInlineOperands[2]; + + struct + { + CORINFO_METHOD_HANDLE gtMethodHandle; + +#if defined(FEATURE_READYTORUN) + // Call target lookup info for method call from a Ready To Run module + CORINFO_CONST_LOOKUP* gtEntryPoint; +#endif // FEATURE_READYTORUN + }; + }; regNumberSmall gtOtherReg; // The second register for multi-reg intrinsics. MultiRegSpillFlags gtSpillFlags; // Spill flags for multi-reg intrinsics. unsigned char gtAuxiliaryJitType; // For intrinsics than need another type (e.g. Avx2.Gather* or SIMD (by element)) @@ -6226,6 +6264,22 @@ struct GenTreeJitIntrinsic : public GenTreeMultiOp NamedIntrinsic gtHWIntrinsicId; public: + CORINFO_METHOD_HANDLE GetMethodHandle() const + { + assert(IsUserCall()); + return gtMethodHandle; + } + + void SetMethodHandle(Compiler* comp, CORINFO_METHOD_HANDLE methodHandle R2RARG(CORINFO_CONST_LOOKUP entryPoint)); + +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP GetEntryPoint() const + { + assert(IsUserCall()); + return *gtEntryPoint; + } +#endif // FEATURE_READYTORUN + //----------------------------------------------------------- // GetRegNumByIdx: Get regNumber of i'th position. // diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index e6b0e5fa72ffb..48dd3cffa1046 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1131,6 +1131,7 @@ bool Compiler::CheckHWIntrinsicImmRange(NamedIntrinsic intrinsic, // clsHnd -- class handle containing the intrinsic function. // method -- method handle of the intrinsic function. // sig -- signature of the intrinsic call +// entryPoint -- The entry point information required for R2R scenarios // mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false // Return Value: @@ -1139,7 +1140,7 @@ bool Compiler::CheckHWIntrinsicImmRange(NamedIntrinsic intrinsic, GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig R2RARG(CORINFO_CONST_LOOKUP* entryPoint), bool mustExpand) { // NextCallRetAddr requires a CALL, so return nullptr. @@ -1241,7 +1242,27 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, unsigned int sizeBytes; simdBaseJitType = getBaseJitTypeAndSizeOfSIMDType(clsHnd, &sizeBytes); - assert((category == HW_Category_Special) || (category == HW_Category_Helper) || (sizeBytes != 0)); + +#if defined(TARGET_ARM64) + if (simdBaseJitType == CORINFO_TYPE_UNDEF && HWIntrinsicInfo::HasScalarInputVariant(intrinsic)) + { + // Did not find a valid vector type. The intrinsic has alternate scalar version. Switch to that. + + assert(sizeBytes == 0); + intrinsic = HWIntrinsicInfo::GetScalarInputVariant(intrinsic); + category = HWIntrinsicInfo::lookupCategory(intrinsic); + isa = HWIntrinsicInfo::lookupIsa(intrinsic); + + simdBaseJitType = sig->retType; + assert(simdBaseJitType != CORINFO_TYPE_VOID); + assert(simdBaseJitType != CORINFO_TYPE_UNDEF); + assert(simdBaseJitType != CORINFO_TYPE_VALUECLASS); + } + else +#endif + { + assert((category == HW_Category_Special) || (category == HW_Category_Helper) || (sizeBytes != 0)); + } } } @@ -1270,6 +1291,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, int immUpperBound = 0; bool hasFullRangeImm = false; bool useFallback = false; + bool setMethodHandle = false; getHWIntrinsicImmOps(intrinsic, sig, &immOp1, &immOp2); @@ -1286,7 +1308,20 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, if (!CheckHWIntrinsicImmRange(intrinsic, simdBaseJitType, immOp2, mustExpand, immLowerBound, immUpperBound, false, &useFallback)) { - return useFallback ? impNonConstFallback(intrinsic, retType, simdBaseJitType) : nullptr; + if (useFallback) + { + return impNonConstFallback(intrinsic, retType, simdBaseJitType); + } + else if (!opts.OptimizationEnabled()) + { + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; + } + else + { + setMethodHandle = true; + } } } #else @@ -1310,7 +1345,20 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, if (!CheckHWIntrinsicImmRange(intrinsic, simdBaseJitType, immOp1, mustExpand, immLowerBound, immUpperBound, hasFullRangeImm, &useFallback)) { - return useFallback ? impNonConstFallback(intrinsic, retType, simdBaseJitType) : nullptr; + if (useFallback) + { + return impNonConstFallback(intrinsic, retType, simdBaseJitType); + } + else if (!opts.OptimizationEnabled()) + { + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; + } + else + { + setMethodHandle = true; + } } } @@ -1577,8 +1625,13 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, } else { - retNode = - impSpecialIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, nodeRetType, simdSize, mustExpand); + retNode = impSpecialIntrinsic(intrinsic, clsHnd, method, sig R2RARG(entryPoint), simdBaseJitType, nodeRetType, + simdSize, mustExpand); + } + + if (setMethodHandle && (retNode != nullptr)) + { + retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); } #if defined(TARGET_ARM64) @@ -1597,12 +1650,31 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, return retNode->AsHWIntrinsic()->Op(3); } } + else if (intrinsic == NI_Sve_GetActiveElementCount) + { + GenTree* op2 = retNode->AsHWIntrinsic()->Op(2); + + // HWInstrinsic requires a mask for op2 + if (!varTypeIsMask(op2)) + { + retNode->AsHWIntrinsic()->Op(2) = + gtNewSimdConvertVectorToMaskNode(retType, op2, simdBaseJitType, simdSize); + } + } if (!varTypeIsMask(op1)) { // Op1 input is a vector. HWInstrinsic requires a mask. retNode->AsHWIntrinsic()->Op(1) = gtNewSimdConvertVectorToMaskNode(retType, op1, simdBaseJitType, simdSize); } + + if (HWIntrinsicInfo::IsMultiReg(intrinsic)) + { + assert(HWIntrinsicInfo::IsExplicitMaskedOperation(retNode->AsHWIntrinsic()->GetHWIntrinsicId())); + assert(HWIntrinsicInfo::IsMultiReg(retNode->AsHWIntrinsic()->GetHWIntrinsicId())); + retNode = + impStoreMultiRegValueToVar(retNode, sig->retTypeSigClass DEBUGARG(CorInfoCallConvExtension::Managed)); + } } if (retType != nodeRetType) diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 16c5b770ada4a..e156ef3544cd3 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -232,10 +232,14 @@ enum HWIntrinsicFlag : unsigned int // The intrinsic has an enum operand. Using this implies HW_Flag_HasImmediateOperand. HW_Flag_HasEnumOperand = 0x1000000, + // The intrinsic comes in both vector and scalar variants. During the import stage if the basetype is scalar, + // then the intrinsic should be switched to a scalar only version. + HW_Flag_HasScalarInputVariant = 0x2000000, + #endif // TARGET_XARCH // The intrinsic is a FusedMultiplyAdd intrinsic - HW_Flag_FmaIntrinsic = 0x20000000, + HW_Flag_FmaIntrinsic = 0x40000000, #if defined(TARGET_ARM64) // The intrinsic uses a mask in arg1 to select elements present in the result, and must use a low vector register. @@ -831,6 +835,7 @@ struct HWIntrinsicInfo case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x2: case NI_AdvSimd_LoadAndReplicateToVector64x2: case NI_AdvSimd_Arm64_LoadAndReplicateToVector128x2: + case NI_Sve_Load2xVectorAndUnzip: return 2; case NI_AdvSimd_LoadVector64x3AndUnzip: @@ -841,6 +846,7 @@ struct HWIntrinsicInfo case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x3: case NI_AdvSimd_LoadAndReplicateToVector64x3: case NI_AdvSimd_Arm64_LoadAndReplicateToVector128x3: + case NI_Sve_Load3xVectorAndUnzip: return 3; case NI_AdvSimd_LoadVector64x4AndUnzip: @@ -851,6 +857,7 @@ struct HWIntrinsicInfo case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x4: case NI_AdvSimd_LoadAndReplicateToVector64x4: case NI_AdvSimd_Arm64_LoadAndReplicateToVector128x4: + case NI_Sve_Load4xVectorAndUnzip: return 4; #endif @@ -926,6 +933,41 @@ struct HWIntrinsicInfo return (flags & HW_Flag_HasEnumOperand) != 0; } + static bool HasScalarInputVariant(NamedIntrinsic id) + { + const HWIntrinsicFlag flags = lookupFlags(id); + return (flags & HW_Flag_HasScalarInputVariant) != 0; + } + + static NamedIntrinsic GetScalarInputVariant(NamedIntrinsic id) + { + assert(HasScalarInputVariant(id)); + + switch (id) + { + case NI_Sve_SaturatingDecrementBy16BitElementCount: + return NI_Sve_SaturatingDecrementBy16BitElementCountScalar; + + case NI_Sve_SaturatingDecrementBy32BitElementCount: + return NI_Sve_SaturatingDecrementBy32BitElementCountScalar; + + case NI_Sve_SaturatingDecrementBy64BitElementCount: + return NI_Sve_SaturatingDecrementBy64BitElementCountScalar; + + case NI_Sve_SaturatingIncrementBy16BitElementCount: + return NI_Sve_SaturatingIncrementBy16BitElementCountScalar; + + case NI_Sve_SaturatingIncrementBy32BitElementCount: + return NI_Sve_SaturatingIncrementBy32BitElementCountScalar; + + case NI_Sve_SaturatingIncrementBy64BitElementCount: + return NI_Sve_SaturatingIncrementBy64BitElementCountScalar; + + default: + unreached(); + } + } + #endif // TARGET_ARM64 static bool HasSpecialSideEffect(NamedIntrinsic id) @@ -965,6 +1007,66 @@ struct HWIntrinsicInfo return (flags & HW_Flag_PermuteVar2x) != 0; } #endif // TARGET_XARCH + +#if defined(TARGET_ARM64) + static void GetImmOpsPositions(NamedIntrinsic id, CORINFO_SIG_INFO* sig, int* imm1Pos, int* imm2Pos) + { + switch (id) + { + case NI_AdvSimd_Insert: + case NI_AdvSimd_InsertScalar: + case NI_AdvSimd_LoadAndInsertScalar: + case NI_AdvSimd_LoadAndInsertScalarVector64x2: + case NI_AdvSimd_LoadAndInsertScalarVector64x3: + case NI_AdvSimd_LoadAndInsertScalarVector64x4: + case NI_AdvSimd_Arm64_LoadAndInsertScalar: + case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x2: + case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x3: + case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x4: + { + assert(sig->numArgs == 3); + *imm1Pos = 1; + break; + } + + case NI_AdvSimd_Arm64_InsertSelectedScalar: + { + assert(sig->numArgs == 4); + *imm1Pos = 2; + *imm2Pos = 0; + break; + } + + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: + { + assert(sig->numArgs == 3); + *imm1Pos = 1; + *imm2Pos = 0; + break; + } + + default: + { + assert(sig->numArgs > 0); + *imm1Pos = 0; + break; + } + } + } +#endif // TARGET_ARM64 }; #ifdef TARGET_ARM64 diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index ec4bc0ee5dd95..095f31246d0c6 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -233,33 +233,7 @@ void Compiler::getHWIntrinsicImmOps(NamedIntrinsic intrinsic, int imm1Pos = -1; int imm2Pos = -1; - switch (intrinsic) - { - case NI_AdvSimd_Insert: - case NI_AdvSimd_InsertScalar: - case NI_AdvSimd_LoadAndInsertScalar: - case NI_AdvSimd_LoadAndInsertScalarVector64x2: - case NI_AdvSimd_LoadAndInsertScalarVector64x3: - case NI_AdvSimd_LoadAndInsertScalarVector64x4: - case NI_AdvSimd_Arm64_LoadAndInsertScalar: - case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x2: - case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x3: - case NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x4: - assert(sig->numArgs == 3); - imm1Pos = 1; - break; - - case NI_AdvSimd_Arm64_InsertSelectedScalar: - assert(sig->numArgs == 4); - imm1Pos = 2; - imm2Pos = 0; - break; - - default: - assert(sig->numArgs > 0); - imm1Pos = 0; - break; - } + HWIntrinsicInfo::GetImmOpsPositions(intrinsic, sig, &imm1Pos, &imm2Pos); if (imm1Pos >= 0) { @@ -447,6 +421,33 @@ void HWIntrinsicInfo::lookupImmBounds( immUpperBound = (int)SVE_PATTERN_ALL; break; + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: + if (immNumber == 1) + { + immLowerBound = 1; + immUpperBound = 16; + } + else + { + assert(immNumber == 2); + immLowerBound = (int)SVE_PATTERN_POW2; + immUpperBound = (int)SVE_PATTERN_ALL; + } + break; + default: unreached(); } @@ -482,6 +483,7 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT // clsHnd -- class handle containing the intrinsic function. // method -- method handle of the intrinsic function. // sig -- signature of the intrinsic call. +// entryPoint -- The entry point information required for R2R scenarios // simdBaseJitType -- generic argument of the intrinsic. // retType -- return type of the intrinsic. // mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false @@ -492,7 +494,7 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig R2RARG(CORINFO_CONST_LOOKUP* entryPoint), CorInfoType simdBaseJitType, var_types retType, unsigned simdSize, @@ -512,7 +514,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, return gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic); } - assert(category != HW_Category_Scalar); + bool isScalar = (category == HW_Category_Scalar); assert(!HWIntrinsicInfo::isScalarIsa(HWIntrinsicInfo::lookupIsa(intrinsic))); assert(numArgs >= 0); @@ -526,6 +528,10 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, GenTree* op3 = nullptr; GenTree* op4 = nullptr; +#ifdef DEBUG + bool isValidScalarIntrinsic = false; +#endif + switch (intrinsic) { case NI_Vector64_Abs: @@ -537,8 +543,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Add: - case NI_Vector128_Add: case NI_Vector64_op_Addition: case NI_Vector128_op_Addition: { @@ -563,34 +567,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_As: - case NI_Vector64_AsByte: - case NI_Vector64_AsDouble: - case NI_Vector64_AsInt16: - case NI_Vector64_AsInt32: - case NI_Vector64_AsInt64: - case NI_Vector64_AsNInt: - case NI_Vector64_AsNUInt: - case NI_Vector64_AsSByte: - case NI_Vector64_AsSingle: - case NI_Vector64_AsUInt16: - case NI_Vector64_AsUInt32: - case NI_Vector64_AsUInt64: - case NI_Vector128_As: - case NI_Vector128_AsByte: - case NI_Vector128_AsDouble: - case NI_Vector128_AsInt16: - case NI_Vector128_AsInt32: - case NI_Vector128_AsInt64: - case NI_Vector128_AsNInt: - case NI_Vector128_AsNUInt: - case NI_Vector128_AsSByte: - case NI_Vector128_AsSingle: - case NI_Vector128_AsUInt16: - case NI_Vector128_AsUInt32: - case NI_Vector128_AsUInt64: case NI_Vector128_AsVector: - case NI_Vector128_AsVector4: { assert(!sig->hasThis()); assert(numArgs == 1); @@ -705,8 +682,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_BitwiseAnd: - case NI_Vector128_BitwiseAnd: case NI_Vector64_op_BitwiseAnd: case NI_Vector128_op_BitwiseAnd: { @@ -719,8 +694,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_BitwiseOr: - case NI_Vector128_BitwiseOr: case NI_Vector64_op_BitwiseOr: case NI_Vector128_op_BitwiseOr: { @@ -1089,8 +1062,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Divide: - case NI_Vector128_Divide: case NI_Vector64_op_Division: case NI_Vector128_op_Division: { @@ -1147,8 +1118,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_EqualsAll: - case NI_Vector128_EqualsAll: case NI_Vector64_op_Equality: case NI_Vector128_op_Equality: { @@ -1394,14 +1363,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_get_Zero: - case NI_Vector128_get_Zero: - { - assert(sig->numArgs == 0); - retNode = gtNewZeroConNode(retType); - break; - } - case NI_Vector64_GetElement: case NI_Vector128_GetElement: { @@ -1587,8 +1548,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_AdvSimd_LoadVector64: case NI_AdvSimd_LoadVector128: - case NI_Vector64_Load: - case NI_Vector128_Load: case NI_Vector64_LoadUnsafe: case NI_Vector128_LoadUnsafe: { @@ -1694,8 +1653,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Multiply: - case NI_Vector128_Multiply: case NI_Vector64_op_Multiply: case NI_Vector128_op_Multiply: { @@ -1759,8 +1716,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Negate: - case NI_Vector128_Negate: case NI_Vector64_op_UnaryNegation: case NI_Vector128_op_UnaryNegation: { @@ -1770,8 +1725,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_OnesComplement: - case NI_Vector128_OnesComplement: case NI_Vector64_op_OnesComplement: case NI_Vector128_op_OnesComplement: { @@ -1794,16 +1747,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_op_UnaryPlus: - case NI_Vector128_op_UnaryPlus: - { - assert(sig->numArgs == 1); - retNode = impSIMDPopStack(); - break; - } - - case NI_Vector64_Subtract: - case NI_Vector128_Subtract: case NI_Vector64_op_Subtraction: case NI_Vector128_op_Subtraction: { @@ -1816,8 +1759,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ShiftLeft: - case NI_Vector128_ShiftLeft: case NI_Vector64_op_LeftShift: case NI_Vector128_op_LeftShift: { @@ -1830,8 +1771,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ShiftRightArithmetic: - case NI_Vector128_ShiftRightArithmetic: case NI_Vector64_op_RightShift: case NI_Vector128_op_RightShift: { @@ -1845,8 +1784,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_ShiftRightLogical: - case NI_Vector128_ShiftRightLogical: case NI_Vector64_op_UnsignedRightShift: case NI_Vector128_op_UnsignedRightShift: { @@ -1867,9 +1804,23 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, GenTree* indices = impStackTop(0).val; - if (!indices->IsVectorConst()) + if (!indices->IsVectorConst() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) { - // TODO-ARM64-CQ: Handling non-constant indices is a bit more complex + assert(sig->numArgs == 2); + + if (!opts.OptimizationEnabled()) + { + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; + } + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + + retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); break; } @@ -1916,9 +1867,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Store: case NI_Vector64_StoreUnsafe: - case NI_Vector128_Store: case NI_Vector128_StoreUnsafe: { assert(retType == TYP_VOID); @@ -2084,6 +2033,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 3); assert(retType == TYP_VOID); + if (!mustExpand && !impStackTop(0).val->IsCnsIntOrI() && (impStackTop(1).val->TypeGet() == TYP_STRUCT)) + { + // TODO-ARM64-CQ: Support rewriting nodes that involves + // GenTreeFieldList as user calls during rationalization + return nullptr; + } + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); @@ -2195,9 +2151,21 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (!indexOp->OperIsConst()) { - // TODO-ARM64-CQ: We should always import these like we do with GetElement - // If index is not constant use software fallback. - return nullptr; + if (!opts.OptimizationEnabled()) + { + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; + } + + op3 = impPopStack().val; + op2 = impPopStack().val; + op1 = impSIMDPopStack(); + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, simdBaseJitType, simdSize); + + retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); + break; } ssize_t imm8 = indexOp->AsIntCon()->IconValue(); @@ -2237,8 +2205,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector64_Xor: - case NI_Vector128_Xor: case NI_Vector64_op_ExclusiveOr: case NI_Vector128_op_ExclusiveOr: { @@ -2296,6 +2262,35 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, retNode = impStoreMultiRegValueToVar(op1, sig->retTypeSigClass DEBUGARG(CorInfoCallConvExtension::Managed)); break; } + + case NI_Sve_Load2xVectorAndUnzip: + case NI_Sve_Load3xVectorAndUnzip: + case NI_Sve_Load4xVectorAndUnzip: + { + info.compNeedsConsecutiveRegisters = true; + + assert(sig->numArgs == 2); + + op2 = impPopStack().val; + op1 = impPopStack().val; + + if (op2->OperIs(GT_CAST)) + { + // Although the API specifies a pointer, if what we have is a BYREF, that's what + // we really want, so throw away the cast. + if (op2->gtGetOp1()->TypeGet() == TYP_BYREF) + { + op2 = op2->gtGetOp1(); + } + } + + assert(HWIntrinsicInfo::IsMultiReg(intrinsic)); + assert(HWIntrinsicInfo::IsExplicitMaskedOperation(intrinsic)); + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + break; + } + case NI_AdvSimd_LoadAndInsertScalarVector64x2: case NI_AdvSimd_LoadAndInsertScalarVector64x3: case NI_AdvSimd_LoadAndInsertScalarVector64x4: @@ -2305,6 +2300,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 3); + if (!mustExpand && !impStackTop(1).val->IsCnsIntOrI()) + { + // TODO-ARM64-CQ: Support rewriting nodes that involves + // GenTreeFieldList as user calls during rationalization + return nullptr; + } + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); @@ -2436,6 +2438,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); var_types argType = TYP_UNKNOWN; CORINFO_CLASS_HANDLE argClass = NO_CLASS_HANDLE; + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); op3 = impPopStack().val; unsigned fieldCount = info.compCompHnd->getClassNumInstanceFields(argClass); @@ -2507,12 +2510,96 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: +#ifdef DEBUG + isValidScalarIntrinsic = true; + FALLTHROUGH; +#endif + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + + { + assert(sig->numArgs == 3); + + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; + CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); + CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); + var_types argType = TYP_UNKNOWN; + CORINFO_CLASS_HANDLE argClass = NO_CLASS_HANDLE; + int immLowerBound = 0; + int immUpperBound = 0; + + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); + op3 = getArgForHWIntrinsic(argType, argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); + op2 = getArgForHWIntrinsic(argType, argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass))); + op1 = impPopStack().val; + + assert(HWIntrinsicInfo::isImmOp(intrinsic, op2)); + HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 1, &immLowerBound, &immUpperBound); + op2 = addRangeCheckIfNeeded(intrinsic, op2, (!op2->IsCnsIntOrI()), immLowerBound, immUpperBound); + + assert(HWIntrinsicInfo::isImmOp(intrinsic, op3)); + HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 2, &immLowerBound, &immUpperBound); + op3 = addRangeCheckIfNeeded(intrinsic, op3, (!op3->IsCnsIntOrI()), immLowerBound, immUpperBound); + + retNode = isScalar ? gtNewScalarHWIntrinsicNode(retType, op1, op2, op3, intrinsic) + : gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, simdBaseJitType, simdSize); + + retNode->AsHWIntrinsic()->SetSimdBaseJitType(simdBaseJitType); + break; + } + + case NI_Sve_SaturatingDecrementByActiveElementCount: + case NI_Sve_SaturatingIncrementByActiveElementCount: + { + assert(sig->numArgs == 2); + + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; + CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); + var_types argType = TYP_UNKNOWN; + CORINFO_CLASS_HANDLE argClass = NO_CLASS_HANDLE; + + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); + op2 = getArgForHWIntrinsic(argType, argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass))); + op1 = impPopStack().val; + + CorInfoType op1BaseJitType = getBaseJitTypeOfSIMDType(argClass); + + // HWInstrinsic requires a mask for op2 + if (!varTypeIsMask(op2)) + { + op2 = gtNewSimdConvertVectorToMaskNode(retType, op2, simdBaseJitType, simdSize); + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); + + retNode->AsHWIntrinsic()->SetSimdBaseJitType(simdBaseJitType); + retNode->AsHWIntrinsic()->SetAuxiliaryJitType(op1BaseJitType); + break; + } + default: { return nullptr; } } + assert(!isScalar || isValidScalarIntrinsic); + return retNode; } diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index 9536405b8ad29..23b09c0cef751 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -23,7 +23,6 @@ // codeGen -- an instance of CodeGen class. // immOp -- an immediate operand of the intrinsic. // intrin -- a hardware intrinsic tree node. -// immNumber -- which immediate operand to use (most intrinsics only have one). // // Note: This class is designed to be used in the following way // HWIntrinsicImmOpHelper helper(this, immOp, intrin); @@ -36,10 +35,7 @@ // This allows to combine logic for cases when immOp->isContainedIntOrIImmed() is either true or false in a form // of a for-loop. // -CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* codeGen, - GenTree* immOp, - GenTreeHWIntrinsic* intrin, - int immNumber /* = 1 */) +CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin) : codeGen(codeGen) , endLabel(nullptr) , nonZeroLabel(nullptr) @@ -79,12 +75,12 @@ CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* code const unsigned int indexedElementSimdSize = genTypeSize(indexedElementOpType); HWIntrinsicInfo::lookupImmBounds(intrin->GetHWIntrinsicId(), indexedElementSimdSize, - intrin->GetSimdBaseType(), immNumber, &immLowerBound, &immUpperBound); + intrin->GetSimdBaseType(), 1, &immLowerBound, &immUpperBound); } else { HWIntrinsicInfo::lookupImmBounds(intrin->GetHWIntrinsicId(), intrin->GetSimdSize(), - intrin->GetSimdBaseType(), immNumber, &immLowerBound, &immUpperBound); + intrin->GetSimdBaseType(), 1, &immLowerBound, &immUpperBound); } nonConstImmReg = immOp->GetRegNum(); @@ -109,6 +105,50 @@ CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* code } } +// HWIntrinsicImmOpHelper: Variant constructor of the helper class instance. +// This is used when the immediate does not exist in a GenTree. For example, the immediate has been created +// during codegen from other immediate values. +// +// Arguments: +// codeGen -- an instance of CodeGen class. +// immReg -- the register containing the immediate. +// immLowerBound -- the lower bound of the register. +// immUpperBound -- the lower bound of the register. +// intrin -- a hardware intrinsic tree node. +// +// Note: This instance is designed to be used via the same for loop as the standard constructor. +// +CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper( + CodeGen* codeGen, regNumber immReg, int immLowerBound, int immUpperBound, GenTreeHWIntrinsic* intrin) + : codeGen(codeGen) + , endLabel(nullptr) + , nonZeroLabel(nullptr) + , immValue(immLowerBound) + , immLowerBound(immLowerBound) + , immUpperBound(immUpperBound) + , nonConstImmReg(immReg) + , branchTargetReg(REG_NA) +{ + assert(codeGen != nullptr); + + if (TestImmOpZeroOrOne()) + { + nonZeroLabel = codeGen->genCreateTempLabel(); + } + else + { + // At the moment, this helper supports only intrinsics that correspond to one machine instruction. + // If we ever encounter an intrinsic that is either lowered into multiple instructions or + // the number of instructions that correspond to each case is unknown apriori - we can extend support to + // these by + // using the same approach as in hwintrinsicxarch.cpp - adding an additional indirection level in form of a + // branch table. + branchTargetReg = codeGen->internalRegisters.GetSingle(intrin); + } + + endLabel = codeGen->genCreateTempLabel(); +} + //------------------------------------------------------------------------ // EmitBegin: emits the beginning of a "switch" table, no-op if an immediate operand is constant. // @@ -474,7 +514,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) opt); } } - else if (targetReg == embMaskOp1Reg) + else if (emitter::isVectorRegister(embMaskOp1Reg) && (targetReg == embMaskOp1Reg)) { // target != falseValue, but we do not want to overwrite target with `embMaskOp1Reg`. // We will first do the predicate operation and then do conditionalSelect inactive @@ -768,10 +808,17 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) else if (HWIntrinsicInfo::IsScalable(intrin.id)) { assert(!node->IsEmbMaskOp()); - // This generates an unpredicated version - // Predicated should be taken care above `intrin.op2->IsEmbMaskOp()` - GetEmitter()->emitIns_R_R_R(ins, emitSize, targetReg, op1Reg, op2Reg, opt, - INS_SCALABLE_OPTS_UNPREDICATED); + if (HWIntrinsicInfo::IsExplicitMaskedOperation(intrin.id)) + { + GetEmitter()->emitIns_R_R_R(ins, emitSize, targetReg, op1Reg, op2Reg, opt); + } + else + { + // This generates an unpredicated version + // Implicitly predicated should be taken care above `intrin.op2->IsEmbMaskOp()` + GetEmitter()->emitIns_R_R_R(ins, emitSize, targetReg, op1Reg, op2Reg, opt, + INS_SCALABLE_OPTS_UNPREDICATED); + } } else if (isRMW) { @@ -1360,6 +1407,26 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } break; + case NI_Sve_Load2xVectorAndUnzip: + case NI_Sve_Load3xVectorAndUnzip: + case NI_Sve_Load4xVectorAndUnzip: + { +#ifdef DEBUG + // Validates that consecutive registers were used properly. + + assert(node->GetMultiRegCount(compiler) == (unsigned int)GetEmitter()->insGetSveReg1ListSize(ins)); + + regNumber argReg = targetReg; + for (unsigned int i = 0; i < node->GetMultiRegCount(compiler); i++) + { + assert(argReg == node->GetRegNumByIdx(i)); + argReg = getNextSIMDRegWithWraparound(argReg); + } +#endif // DEBUG + GetEmitter()->emitIns_R_R_R_I(ins, emitSize, targetReg, op1Reg, op2Reg, 0, opt); + break; + } + case NI_Sve_StoreAndZipx2: case NI_Sve_StoreAndZipx3: case NI_Sve_StoreAndZipx4: @@ -1416,6 +1483,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } case NI_Sve_StoreAndZip: + case NI_Sve_StoreNonTemporal: { GetEmitter()->emitIns_R_R_R_I(ins, emitSize, op3Reg, op1Reg, op2Reg, 0, opt); break; @@ -1777,11 +1845,18 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case NI_Sve_ReverseElement: + // Use non-predicated version explicitly + GetEmitter()->emitIns_R_R(ins, emitSize, targetReg, op1Reg, opt, INS_SCALABLE_OPTS_UNPREDICATED); + break; + case NI_Sve_StoreNarrowing: opt = emitter::optGetSveInsOpt(emitTypeSize(intrin.baseType)); GetEmitter()->emitIns_R_R_R_I(ins, emitSize, op3Reg, op1Reg, op2Reg, 0, opt); break; + case NI_Sve_TransposeEven: + case NI_Sve_TransposeOdd: case NI_Sve_UnzipEven: case NI_Sve_UnzipOdd: case NI_Sve_ZipHigh: @@ -1791,6 +1866,119 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) INS_SCALABLE_OPTS_UNPREDICATED); break; + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: + // Use scalar sizes. + emitSize = emitActualTypeSize(node->gtType); + opt = INS_OPTS_NONE; + FALLTHROUGH; + + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + { + assert(isRMW); + if (targetReg != op1Reg) + { + assert(targetReg != op2Reg); + assert(targetReg != op3Reg); + GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); + } + + if (intrin.op2->IsCnsIntOrI() && intrin.op3->IsCnsIntOrI()) + { + // Both immediates are constant, emit the intruction. + + assert(intrin.op2->isContainedIntOrIImmed() && intrin.op3->isContainedIntOrIImmed()); + int scale = (int)intrin.op2->AsIntCon()->gtIconVal; + insSvePattern pattern = (insSvePattern)intrin.op3->AsIntCon()->gtIconVal; + GetEmitter()->emitIns_R_PATTERN_I(ins, emitSize, targetReg, pattern, scale, opt); + } + else + { + // Use the helper to generate a table. The table can only use a single lookup value, therefore + // the two immediates scale (1 to 16, in op2Reg) and pattern (0 to 31, in op3reg) must be + // combined to a single value (0 to 511) + + assert(!intrin.op2->isContainedIntOrIImmed() && !intrin.op3->isContainedIntOrIImmed()); + + emitAttr scalarSize = emitActualTypeSize(node->GetSimdBaseType()); + + // Combine the two immediates into op2Reg. + // Reduce scale to have a lower bound of 0. + GetEmitter()->emitIns_R_R_I(INS_sub, scalarSize, op2Reg, op2Reg, 1); + // Shift pattern left to be out of range of scale. + GetEmitter()->emitIns_R_R_I(INS_lsl, scalarSize, op3Reg, op3Reg, 4); + // Combine the two values by ORing. + GetEmitter()->emitIns_R_R_R(INS_orr, scalarSize, op2Reg, op2Reg, op3Reg); + + // Generate the table using the combined immediate. + HWIntrinsicImmOpHelper helper(this, op2Reg, 0, 511, node); + for (helper.EmitBegin(); !helper.Done(); helper.EmitCaseEnd()) + { + // Extract scale and pattern from the immediate + const int value = helper.ImmValue(); + const int scale = (value & 0xF) + 1; + const insSvePattern pattern = (insSvePattern)(value >> 4); + GetEmitter()->emitIns_R_PATTERN_I(ins, emitSize, targetReg, pattern, scale, opt); + } + + // Restore the original values in op2Reg and op3Reg. + GetEmitter()->emitIns_R_R_I(INS_and, scalarSize, op2Reg, op2Reg, 0xF); + GetEmitter()->emitIns_R_R_I(INS_lsr, scalarSize, op3Reg, op3Reg, 4); + GetEmitter()->emitIns_R_R_I(INS_add, scalarSize, op2Reg, op2Reg, 1); + } + break; + } + + case NI_Sve_SaturatingDecrementByActiveElementCount: + case NI_Sve_SaturatingIncrementByActiveElementCount: + { + // RMW semantics + if (targetReg != op1Reg) + { + assert(targetReg != op2Reg); + GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); + } + + // Switch instruction if arg1 is unsigned. + if (varTypeIsUnsigned(node->GetAuxiliaryType())) + { + ins = + (intrin.id == NI_Sve_SaturatingDecrementByActiveElementCount) ? INS_sve_uqdecp : INS_sve_uqincp; + } + + // If this is the scalar variant, get the correct size. + if (!varTypeIsSIMD(node->gtType)) + { + emitSize = emitActualTypeSize(intrin.op1); + } + + GetEmitter()->emitIns_R_R(ins, emitSize, targetReg, op2Reg, opt); + break; + } + case NI_Sve_Compute8BitAddresses: + case NI_Sve_Compute16BitAddresses: + case NI_Sve_Compute32BitAddresses: + case NI_Sve_Compute64BitAddresses: + { + static_assert_no_msg(AreContiguous(NI_Sve_Compute8BitAddresses, NI_Sve_Compute16BitAddresses, + NI_Sve_Compute32BitAddresses, NI_Sve_Compute64BitAddresses)); + + GetEmitter()->emitInsSve_R_R_R_I(ins, EA_SCALABLE, targetReg, op1Reg, op2Reg, + (intrin.id - NI_Sve_Compute8BitAddresses), opt, + INS_SCALABLE_OPTS_LSL_N); + break; + } default: unreached(); } diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index 8ef9517c344d5..4aabaeda47cdd 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -180,6 +180,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) CORINFO_InstructionSet isa = HWIntrinsicInfo::lookupIsa(intrinsicId); HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId); size_t numArgs = node->GetOperandCount(); + GenTree* embMaskNode = nullptr; GenTree* embMaskOp = nullptr; // We need to validate that other phases of the compiler haven't introduced unsupported intrinsics @@ -233,6 +234,9 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) op2->ClearContained(); op2->SetRegNum(targetReg); + // Track the original mask node so we can call genProduceReg + embMaskNode = node; + // Fixup all the already initialized variables node = op2->AsHWIntrinsic(); intrinsicId = node->GetHWIntrinsicId(); @@ -696,13 +700,27 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) // Handle an extra operand we need to consume so that // embedded masking can work without making the overall // logic significantly more complex. + + assert(embMaskNode != nullptr); genConsumeReg(embMaskOp); } genProduceReg(node); + + if (embMaskNode != nullptr) + { + // Similarly to the mask operand, we need to handle the + // mask node to ensure everything works correctly, particularly + // lifetimes and spilling if required. Doing it this way avoids + // needing to duplicate all our existing handling. + + assert(embMaskOp != nullptr); + genProduceReg(embMaskNode); + } return; } + assert(embMaskNode == nullptr); assert(embMaskOp == nullptr); switch (isa) @@ -3053,8 +3071,6 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) regNumber targetReg = node->GetRegNum(); - genConsumeMultiOpOperands(node); - regNumber op1NodeReg = op1->GetRegNum(); regNumber op2NodeReg = op2->GetRegNum(); regNumber op3NodeReg = op3->GetRegNum(); @@ -3143,6 +3159,21 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) } } +#ifdef DEBUG + // Use nums are assigned in LIR order but this node is special and doesn't + // actually use operands. Fix up the use nums here to avoid asserts. + unsigned useNum1 = op1->gtUseNum; + unsigned useNum2 = op2->gtUseNum; + unsigned useNum3 = op3->gtUseNum; + emitOp1->gtUseNum = useNum1; + emitOp2->gtUseNum = useNum2; + emitOp3->gtUseNum = useNum3; +#endif + + genConsumeRegs(emitOp1); + genConsumeRegs(emitOp2); + genConsumeRegs(emitOp3); + assert(ins != INS_invalid); genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, emitOp1->GetRegNum(), emitOp2->GetRegNum(), emitOp3, instOptions); genProduceReg(node); diff --git a/src/coreclr/jit/hwintrinsiclistarm64.h b/src/coreclr/jit/hwintrinsiclistarm64.h index a207ac5bc6040..d3c880f38e0fd 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64.h +++ b/src/coreclr/jit/hwintrinsiclistarm64.h @@ -16,23 +16,7 @@ // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // Vector64 Intrinsics HARDWARE_INTRINSIC(Vector64, Abs, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Add, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, AndNot, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, As, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsByte, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsDouble, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsInt16, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsInt32, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsInt64, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsNInt, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsNUInt, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsSByte, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsSingle, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsUInt16, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsUInt32, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, AsUInt64, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, BitwiseAnd, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, BitwiseOr, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Ceiling, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, ConditionalSelect, 8, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, ConvertToDouble, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -49,10 +33,8 @@ HARDWARE_INTRINSIC(Vector64, Create, HARDWARE_INTRINSIC(Vector64, CreateScalar, 8, -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) HARDWARE_INTRINSIC(Vector64, CreateScalarUnsafe, 8, 1, true, {INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_invalid, INS_invalid, INS_fmov, INS_invalid}, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_SupportsContainment) HARDWARE_INTRINSIC(Vector64, CreateSequence, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Divide, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Dot, 8, 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|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, Equals, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, EqualsAll, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, EqualsAny, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, ExtractMostSignificantBits, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, Floor, 8, 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_InvalidNodeId) @@ -60,7 +42,6 @@ HARDWARE_INTRINSIC(Vector64, FusedMultiplyAdd, HARDWARE_INTRINSIC(Vector64, get_AllBitsSet, 8, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, get_Indices, 8, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, get_One, 8, 0, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, get_Zero, 8, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, GetElement, 8, 2, true, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, GreaterThan, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, GreaterThanAll, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -74,17 +55,13 @@ HARDWARE_INTRINSIC(Vector64, LessThanAny, HARDWARE_INTRINSIC(Vector64, LessThanOrEqual, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, LessThanOrEqualAll, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, LessThanOrEqualAny, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, Load, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, LoadAligned, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, LoadAlignedNonTemporal, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, LoadUnsafe, 8, -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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Max, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Min, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Multiply, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, MultiplyAddEstimate, 8, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Narrow, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Negate, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, OnesComplement, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, op_Addition, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, op_BitwiseAnd, 8, 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_InvalidNodeId|HW_Flag_Commutative) HARDWARE_INTRINSIC(Vector64, op_BitwiseOr, 8, 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_InvalidNodeId|HW_Flag_Commutative) @@ -98,18 +75,12 @@ HARDWARE_INTRINSIC(Vector64, op_OnesComplement, HARDWARE_INTRINSIC(Vector64, op_RightShift, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, op_Subtraction, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, op_UnaryNegation, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, op_UnaryPlus, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, op_UnsignedRightShift, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, ShiftLeft, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, ShiftRightArithmetic, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, ShiftRightLogical, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Shuffle, 8, -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_InvalidNodeId) +HARDWARE_INTRINSIC(Vector64, Shuffle, 8, -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_SpecialCodeGen) HARDWARE_INTRINSIC(Vector64, Sqrt, 8, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector64, Store, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, StoreAligned, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, StoreAlignedNonTemporal, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, StoreUnsafe, 8, -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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector64, Subtract, 8, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector64, Sum, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, ToScalar, 8, 1, true, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector64, ToVector128, 8, 1, true, {INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov}, HW_Category_SIMD, HW_Flag_SpecialCodeGen) @@ -117,7 +88,6 @@ HARDWARE_INTRINSIC(Vector64, ToVector128Unsafe, HARDWARE_INTRINSIC(Vector64, WidenLower, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, WidenUpper, 8, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector64, WithElement, 8, 3, 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_BaseTypeFromFirstArg|HW_Flag_SpecialImport) -HARDWARE_INTRINSIC(Vector64, Xor, 8, 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_InvalidNodeId) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg EncodesExtraTypeArg Instructions Category Flags @@ -126,28 +96,11 @@ HARDWARE_INTRINSIC(Vector64, Xor, // Vector128 Intrinsics HARDWARE_INTRINSIC(Vector128, Abs, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Add, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AndNot, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, As, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsByte, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsDouble, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt16, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt32, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt64, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsNInt, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsNUInt, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsSByte, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsSingle, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt16, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt32, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt64, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector2, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector3, 16, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_mov, INS_invalid}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport) -HARDWARE_INTRINSIC(Vector128, AsVector4, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector128, -1, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, BitwiseAnd, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, BitwiseOr, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Ceiling, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ConditionalSelect, 16, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ConvertToDouble, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -164,10 +117,8 @@ HARDWARE_INTRINSIC(Vector128, Create, HARDWARE_INTRINSIC(Vector128, CreateScalar, 16, -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) HARDWARE_INTRINSIC(Vector128, CreateScalarUnsafe, 16, 1, true, {INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_fmov, INS_fmov}, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_SupportsContainment) HARDWARE_INTRINSIC(Vector128, CreateSequence, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Divide, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Dot, 16, 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|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, Equals, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, EqualsAll, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, EqualsAny, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ExtractMostSignificantBits, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, Floor, 16, 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_InvalidNodeId) @@ -175,7 +126,6 @@ HARDWARE_INTRINSIC(Vector128, FusedMultiplyAdd, HARDWARE_INTRINSIC(Vector128, get_AllBitsSet, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, get_Indices, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, get_One, 16, 0, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, get_Zero, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, GetElement, 16, 2, true, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, GetLower, 16, 1, true, {INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov}, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, GetUpper, 16, 1, true, {INS_ext, INS_ext, INS_ext, INS_ext, INS_ext, INS_ext, INS_ext, INS_ext, INS_ext, INS_ext}, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen) @@ -191,17 +141,13 @@ HARDWARE_INTRINSIC(Vector128, LessThanAny, HARDWARE_INTRINSIC(Vector128, LessThanOrEqual, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LessThanOrEqualAll, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, LessThanOrEqualAny, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, Load, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadAligned, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadAlignedNonTemporal, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadUnsafe, 16, -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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Max, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Min, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Multiply, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, MultiplyAddEstimate, 16, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Narrow, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Negate, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, OnesComplement, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_Addition, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_BitwiseAnd, 16, 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_InvalidNodeId|HW_Flag_Commutative) HARDWARE_INTRINSIC(Vector128, op_BitwiseOr, 16, 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_InvalidNodeId|HW_Flag_Commutative) @@ -216,17 +162,11 @@ HARDWARE_INTRINSIC(Vector128, op_Multiply, HARDWARE_INTRINSIC(Vector128, op_OnesComplement, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_Subtraction, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_UnaryNegation, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, op_UnaryPlus, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftLeft, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftRightArithmetic, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftRightLogical, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -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_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -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_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, Sqrt, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Store, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAligned, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAlignedNonTemporal, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreUnsafe, 16, -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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, Subtract, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Sum, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, true, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, WidenLower, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -234,7 +174,6 @@ HARDWARE_INTRINSIC(Vector128, WidenUpper, HARDWARE_INTRINSIC(Vector128, WithElement, 16, 3, 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_BaseTypeFromFirstArg|HW_Flag_SpecialImport) HARDWARE_INTRINSIC(Vector128, WithLower, 16, 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(Vector128, WithUpper, 16, 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(Vector128, Xor, 16, 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_InvalidNodeId) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg EncodesExtraTypeArg Instructions Category Flags diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index ad264a6a03867..e047fc64447c1 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -26,6 +26,11 @@ HARDWARE_INTRINSIC(Sve, And, HARDWARE_INTRINSIC(Sve, AndAcross, -1, -1, false, {INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_sve_andv, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, BitwiseClear, -1, -1, false, {INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, BooleanNot, -1, -1, false, {INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Compact, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Compute8BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, Compute16BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, Compute32BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, Compute64BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, ConditionalSelect, -1, 3, true, {INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_SupportsContainment) HARDWARE_INTRINSIC(Sve, Count16BitElements, 0, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cnth, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_NoFloatingPointUsed) HARDWARE_INTRINSIC(Sve, Count32BitElements, 0, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cntw, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_NoFloatingPointUsed) @@ -68,33 +73,61 @@ HARDWARE_INTRINSIC(Sve, FusedMultiplyAddNegated, HARDWARE_INTRINSIC(Sve, FusedMultiplySubtract, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmls, INS_sve_fmls}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, FusedMultiplySubtractBySelectedScalar, -1, 4, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmls, INS_sve_fmls}, HW_Category_SIMDByIndexedElement, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasRMWSemantics|HW_Flag_FmaIntrinsic|HW_Flag_LowVectorOperation) HARDWARE_INTRINSIC(Sve, FusedMultiplySubtractNegated, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fnmls, INS_sve_fnmls}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, GetActiveElementCount, -1, 2, true, {INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ExplicitMaskedOperation) HARDWARE_INTRINSIC(Sve, LeadingSignCount, -1, -1, false, {INS_sve_cls, INS_invalid, INS_sve_cls, INS_invalid, INS_sve_cls, INS_invalid, INS_sve_cls, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LeadingZeroCount, -1, -1, false, {INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_sve_clz, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVector, -1, 2, true, {INS_sve_ld1b, INS_sve_ld1b, INS_sve_ld1h, INS_sve_ld1h, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1d, INS_sve_ld1d, INS_sve_ld1w, INS_sve_ld1d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToInt16, -1, 1, false, {INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToInt32, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToUInt16, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToUInt32, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorByteNonFaultingZeroExtendToUInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1b, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToInt16, -1, 2, false, {INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToUInt16, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToUInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorByteZeroExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt16NonFaultingSignExtendToInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt16NonFaultingSignExtendToInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sh, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt16NonFaultingSignExtendToUInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sh, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt16NonFaultingSignExtendToUInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sh, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt16SignExtendToInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt16SignExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sh, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt16SignExtendToUInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sh, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt16SignExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sh, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt32NonFaultingSignExtendToInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sw, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorInt32NonFaultingSignExtendToUInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sw, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt32SignExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sw, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorInt32SignExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sw, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToInt16, -1, -1, false, {INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToUInt16, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToUInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorSByteNonFaultingSignExtendToUInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1sb, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToInt16, -1, 2, false, {INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToUInt16, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToUInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorSByteSignExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sb, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt16NonFaultingZeroExtendToInt32, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1h, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt16NonFaultingZeroExtendToInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1h, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt16NonFaultingZeroExtendToUInt32, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1h, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt16NonFaultingZeroExtendToUInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1h, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt16ZeroExtendToInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1h, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt16ZeroExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1h, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt16ZeroExtendToUInt32, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1h, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt16ZeroExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1h, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt32NonFaultingZeroExtendToInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1w, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, LoadVectorUInt32NonFaultingZeroExtendToUInt64, -1, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldnf1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt32ZeroExtendToInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, LoadVectorUInt32ZeroExtendToUInt64, -1, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Load2xVectorAndUnzip, -1, 2, true, {INS_sve_ld2b, INS_sve_ld2b, INS_sve_ld2h, INS_sve_ld2h, INS_sve_ld2w, INS_sve_ld2w, INS_sve_ld2d, INS_sve_ld2d, INS_sve_ld2w, INS_sve_ld2d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_MultiReg|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Sve, Load3xVectorAndUnzip, -1, 2, true, {INS_sve_ld3b, INS_sve_ld3b, INS_sve_ld3h, INS_sve_ld3h, INS_sve_ld3w, INS_sve_ld3w, INS_sve_ld3d, INS_sve_ld3d, INS_sve_ld3w, INS_sve_ld3d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_MultiReg|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Sve, Load4xVectorAndUnzip, -1, 2, true, {INS_sve_ld4b, INS_sve_ld4b, INS_sve_ld4h, INS_sve_ld4h, INS_sve_ld4w, INS_sve_ld4w, INS_sve_ld4d, INS_sve_ld4d, INS_sve_ld4w, INS_sve_ld4d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_MultiReg|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Sve, Max, -1, -1, false, {INS_sve_smax, INS_sve_umax, INS_sve_smax, INS_sve_umax, INS_sve_smax, INS_sve_umax, INS_sve_smax, INS_sve_umax, INS_sve_fmax, INS_sve_fmax}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, MaxAcross, -1, -1, false, {INS_sve_smaxv, INS_sve_umaxv, INS_sve_smaxv, INS_sve_umaxv, INS_sve_smaxv, INS_sve_umaxv, INS_sve_smaxv, INS_sve_umaxv, INS_sve_fmaxv, INS_sve_fmaxv}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, MaxNumber, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmaxnm, INS_sve_fmaxnm}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) @@ -112,6 +145,20 @@ HARDWARE_INTRINSIC(Sve, Negate, HARDWARE_INTRINSIC(Sve, Or, -1, -1, false, {INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, OrAcross, -1, -1, false, {INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, PopCount, -1, -1, false, {INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, ReverseElement, -1, 1, true, {INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev, INS_sve_rev}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, ReverseElement16, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_revh, INS_sve_revh, INS_sve_revh, INS_sve_revh, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, ReverseElement32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_revw, INS_sve_revw, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, ReverseElement8, -1, -1, false, {INS_invalid, INS_invalid, INS_sve_revb, INS_sve_revb, INS_sve_revb, INS_sve_revb, INS_sve_revb, INS_sve_revb, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy16BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_sve_sqdech, INS_sve_uqdech, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy32BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecw, INS_sve_uqdecw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy64BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecd, INS_sve_uqdecd, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy8BitElementCount, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecb, INS_sve_uqdecb, INS_sve_sqdecb, INS_sve_uqdecb, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementByActiveElementCount, -1, 2, true, {INS_invalid, INS_sve_sqdecp, INS_sve_sqdecp, INS_sve_sqdecp, INS_sve_sqdecp, INS_sve_sqdecp, INS_sve_sqdecp, INS_sve_sqdecp, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy16BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_sve_sqinch, INS_sve_uqinch, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy32BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincw, INS_sve_uqincw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy64BitElementCount, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincd, INS_sve_uqincd, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy8BitElementCount, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincb, INS_sve_uqincb, INS_sve_sqincb, INS_sve_uqincb, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementByActiveElementCount, -1, 2, true, {INS_invalid, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, SignExtend16, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sxth, INS_invalid, INS_sve_sxth, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, SignExtend32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sxtw, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, SignExtend8, -1, -1, false, {INS_invalid, INS_invalid, INS_sve_sxtb, INS_invalid, INS_sve_sxtb, INS_invalid, INS_sve_sxtb, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) @@ -119,8 +166,11 @@ HARDWARE_INTRINSIC(Sve, SignExtendWideningLower, HARDWARE_INTRINSIC(Sve, SignExtendWideningUpper, -1, 1, true, {INS_sve_sunpkhi, INS_invalid, INS_sve_sunpkhi, INS_invalid, INS_sve_sunpkhi, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Sve, StoreAndZip, -1, 3, true, {INS_sve_st1b, INS_sve_st1b, INS_sve_st1h, INS_sve_st1h, INS_sve_st1w, INS_sve_st1w, INS_sve_st1d, INS_sve_st1d, INS_sve_st1w, INS_sve_st1d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ExplicitMaskedOperation|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, StoreNarrowing, -1, 3, true, {INS_sve_st1b, INS_sve_st1b, INS_sve_st1h, INS_sve_st1h, INS_sve_st1w, INS_sve_st1w, INS_sve_st1d, INS_sve_st1d, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ExplicitMaskedOperation|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, StoreNonTemporal, -1, 3, true, {INS_sve_stnt1b, INS_sve_stnt1b, INS_sve_stnt1h, INS_sve_stnt1h, INS_sve_stnt1w, INS_sve_stnt1w, INS_sve_stnt1d, INS_sve_stnt1d, INS_sve_stnt1w, INS_sve_stnt1d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ExplicitMaskedOperation|HW_Flag_SpecialCodeGen|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Subtract, -1, 2, true, {INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_sub, INS_sve_fsub, INS_sve_fsub}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, SubtractSaturate, -1, 2, true, {INS_sve_sqsub, INS_sve_uqsub, INS_sve_sqsub, INS_sve_uqsub, INS_sve_sqsub, INS_sve_uqsub, INS_sve_sqsub, INS_sve_uqsub, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, TransposeEven, -1, 2, true, {INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, TransposeOdd, -1, 2, true, {INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, UnzipEven, -1, 2, true, {INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, UnzipOdd, -1, 2, true, {INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2, INS_sve_uzp2}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, Xor, -1, -1, false, {INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_sve_eor, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) @@ -147,6 +197,14 @@ HARDWARE_INTRINSIC(Sve, StoreAndZipx2, HARDWARE_INTRINSIC(Sve, StoreAndZipx3, -1, 3, true, {INS_sve_st3b, INS_sve_st3b, INS_sve_st3h, INS_sve_st3h, INS_sve_st3w, INS_sve_st3w, INS_sve_st3d, INS_sve_st3d, INS_sve_st3w, INS_sve_st3d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) HARDWARE_INTRINSIC(Sve, StoreAndZipx4, -1, 3, true, {INS_sve_st4b, INS_sve_st4b, INS_sve_st4h, INS_sve_st4h, INS_sve_st4w, INS_sve_st4w, INS_sve_st4d, INS_sve_st4d, INS_sve_st4w, INS_sve_st4d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) +// Scalar variants of Saturating*By*BitElementCount. There is 8bit versions as the generic version is scalar only. +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy16BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdech, INS_sve_uqdech, INS_sve_sqdech, INS_sve_uqdech, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy32BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecw, INS_sve_uqdecw, INS_sve_sqdecw, INS_sve_uqdecw, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy64BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecd, INS_sve_uqdecd, INS_sve_sqdecd, INS_sve_uqdecd, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy16BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqinch, INS_sve_uqinch, INS_sve_sqinch, INS_sve_uqinch, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy32BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincw, INS_sve_uqincw, INS_sve_sqincw, INS_sve_uqincw, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy64BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincd, INS_sve_uqincd, INS_sve_sqincd, INS_sve_uqincd, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) + #endif // FEATURE_HW_INTRINSIC diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index aed1d0b1dfb11..578e50f76821c 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -30,28 +30,11 @@ // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // Vector128 Intrinsics HARDWARE_INTRINSIC(Vector128, Abs, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Add, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AndNot, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, As, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsByte, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsDouble, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt16, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt32, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsInt64, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsNInt, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsNUInt, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsSByte, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsSingle, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt16, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt32, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, AsUInt64, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector2, 16, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movsd_simd, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, AsVector3, 16, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movups, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(Vector128, AsVector4, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, AsVector128, -1, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, BitwiseAnd, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, BitwiseOr, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Ceiling, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ConditionalSelect, 16, 3, 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(Vector128, ConvertToDouble, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -68,10 +51,8 @@ HARDWARE_INTRINSIC(Vector128, Create, HARDWARE_INTRINSIC(Vector128, CreateScalar, 16, 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) HARDWARE_INTRINSIC(Vector128, CreateScalarUnsafe, 16, 1, true, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(Vector128, CreateSequence, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Divide, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Dot, 16, 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|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, Equals, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, EqualsAll, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, EqualsAny, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ExtractMostSignificantBits, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, Floor, 16, 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_InvalidNodeId) @@ -79,7 +60,6 @@ HARDWARE_INTRINSIC(Vector128, FusedMultiplyAdd, HARDWARE_INTRINSIC(Vector128, get_AllBitsSet, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, get_Indices, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, get_One, 16, 0, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, get_Zero, 16, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, GetElement, 16, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_extractps, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, GreaterThan, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, GreaterThanAll, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -93,17 +73,13 @@ HARDWARE_INTRINSIC(Vector128, LessThanAny, HARDWARE_INTRINSIC(Vector128, LessThanOrEqual, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LessThanOrEqualAll, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, LessThanOrEqualAny, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, Load, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadAligned, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadAlignedNonTemporal, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, LoadUnsafe, 16, -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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Max, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Min, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Multiply, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, MultiplyAddEstimate, 16, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Narrow, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Negate, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, OnesComplement, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_Addition, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_BitwiseAnd, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_BitwiseOr, 16, 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_InvalidNodeId) @@ -117,18 +93,12 @@ HARDWARE_INTRINSIC(Vector128, op_OnesComplement, HARDWARE_INTRINSIC(Vector128, op_RightShift, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_Subtraction, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_UnaryNegation, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, op_UnaryPlus, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, op_UnsignedRightShift, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftLeft, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftRightArithmetic, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ShiftRightLogical, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -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_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -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_SpecialCodeGen) HARDWARE_INTRINSIC(Vector128, Sqrt, 16, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Store, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAligned, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreAlignedNonTemporal, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, StoreUnsafe, 16, -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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, Subtract, 16, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Sum, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 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|HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(Vector128, ToVector256, 16, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics) @@ -137,7 +107,6 @@ HARDWARE_INTRINSIC(Vector128, ToVector512, HARDWARE_INTRINSIC(Vector128, WidenLower, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, WidenUpper, 16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector128, WithElement, 16, 3, 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_NoContainment|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector128, Xor, 16, 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_InvalidNodeId) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg EncodesExtraTypeArg Instructions Category Flags @@ -145,25 +114,9 @@ HARDWARE_INTRINSIC(Vector128, Xor, // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // Vector256 Intrinsics HARDWARE_INTRINSIC(Vector256, Abs, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, Add, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, AndNot, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, As, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsByte, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsDouble, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsInt16, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsInt32, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsInt64, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsNInt, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsNUInt, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsSByte, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsSingle, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsUInt16, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsUInt32, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, AsUInt64, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, AsVector, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, AsVector256, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, BitwiseAnd, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, BitwiseOr, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, Ceiling, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ConditionalSelect, 32, 3, 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_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ConvertToDouble, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -180,10 +133,8 @@ HARDWARE_INTRINSIC(Vector256, Create, HARDWARE_INTRINSIC(Vector256, CreateScalar, 32, -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_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, CreateScalarUnsafe, 32, 1, true, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, CreateSequence, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, Divide, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Dot, 32, 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|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, Equals, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, EqualsAll, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, EqualsAny, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, ExtractMostSignificantBits, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, Floor, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) @@ -191,7 +142,6 @@ HARDWARE_INTRINSIC(Vector256, FusedMultiplyAdd, HARDWARE_INTRINSIC(Vector256, get_AllBitsSet, 32, 0, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, get_Indices, 32, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, get_One, 32, 0, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, get_Zero, 32, 0, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, GetElement, 32, 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_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, GetLower, 32, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, GetUpper, 32, 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_AvxOnlyCompatible) @@ -207,17 +157,13 @@ HARDWARE_INTRINSIC(Vector256, LessThanAny, HARDWARE_INTRINSIC(Vector256, LessThanOrEqual, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, LessThanOrEqualAny, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, LessThanOrEqualAll, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector256, Load, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, LoadAligned, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, LoadAlignedNonTemporal, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, LoadUnsafe, 32, -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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, Max, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Min, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, Multiply, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, MultiplyAddEstimate, 32, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Narrow, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, Negate, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, OnesComplement, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_Addition, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, op_BitwiseAnd, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_BitwiseOr, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) @@ -231,18 +177,12 @@ HARDWARE_INTRINSIC(Vector256, op_OnesComplement, HARDWARE_INTRINSIC(Vector256, op_RightShift, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, op_Subtraction, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, op_UnaryNegation, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, op_UnaryPlus, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, op_UnsignedRightShift, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, ShiftLeft, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, ShiftRightArithmetic, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, ShiftRightLogical, 32, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector256, Shuffle, 32, -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_InvalidNodeId) +HARDWARE_INTRINSIC(Vector256, Shuffle, 32, -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_SpecialCodeGen) HARDWARE_INTRINSIC(Vector256, Sqrt, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, Store, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, StoreAligned, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, StoreAlignedNonTemporal, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, StoreUnsafe, 32, -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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, Subtract, 32, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector256, Sum, 32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector256, ToScalar, 32, 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|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, ToVector512, 32, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) @@ -252,7 +192,6 @@ HARDWARE_INTRINSIC(Vector256, WidenUpper, HARDWARE_INTRINSIC(Vector256, WithElement, 32, 3, 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_NoContainment|HW_Flag_BaseTypeFromFirstArg|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, WithLower, 32, 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|HW_Flag_AvxOnlyCompatible) HARDWARE_INTRINSIC(Vector256, WithUpper, 32, 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|HW_Flag_AvxOnlyCompatible) -HARDWARE_INTRINSIC(Vector256, Xor, 32, 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_InvalidNodeId|HW_Flag_AvxOnlyCompatible) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg EncodesExtraTypeArg Instructions Category Flags @@ -260,26 +199,10 @@ HARDWARE_INTRINSIC(Vector256, Xor, // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // Vector512 Intrinsics HARDWARE_INTRINSIC(Vector512, Abs, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, Add, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, AndNot, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, As, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsByte, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsDouble, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsInt16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsInt32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsInt64, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsNInt, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsNUInt, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsSByte, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsSingle, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsUInt16, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsUInt32, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, AsUInt64, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, AsVector, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, AsVector512, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, BitwiseAnd, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, ConditionalSelect, 64, 3, 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, BitwiseOr, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Ceiling, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Create, 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) HARDWARE_INTRINSIC(Vector512, CreateScalar, 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) @@ -295,9 +218,7 @@ HARDWARE_INTRINSIC(Vector512, ConvertToUInt32, HARDWARE_INTRINSIC(Vector512, ConvertToUInt32Native, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, ConvertToUInt64, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, ConvertToUInt64Native, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, Divide, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Equals, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, EqualsAll, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, EqualsAny, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, ExtractMostSignificantBits, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, Floor, 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_InvalidNodeId) @@ -305,7 +226,6 @@ HARDWARE_INTRINSIC(Vector512, FusedMultiplyAdd, HARDWARE_INTRINSIC(Vector512, get_AllBitsSet, 64, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, get_Indices, 64, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, get_One, 64, 0, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, get_Zero, 64, 0, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, GetElement, 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) HARDWARE_INTRINSIC(Vector512, GetLower, 64, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Vector512, GetLower128, 64, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) @@ -322,17 +242,13 @@ HARDWARE_INTRINSIC(Vector512, LessThanAny, HARDWARE_INTRINSIC(Vector512, LessThanOrEqual, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, LessThanOrEqualAll, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, LessThanOrEqualAny, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(Vector512, Load, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, LoadAligned, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, LoadAlignedNonTemporal, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, LoadUnsafe, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Max, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Min, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, Multiply, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, MultiplyAddEstimate, 64, 3, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, Narrow, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, Negate, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, OnesComplement, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_Addition, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_BitwiseAnd, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_BitwiseOr, 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_InvalidNodeId) @@ -346,18 +262,12 @@ HARDWARE_INTRINSIC(Vector512, op_OnesComplement, HARDWARE_INTRINSIC(Vector512, op_RightShift, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_Subtraction, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_UnaryNegation, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, op_UnaryPlus, 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_InvalidNodeId) HARDWARE_INTRINSIC(Vector512, op_UnsignedRightShift, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, ShiftLeft, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, ShiftRightArithmetic, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, ShiftRightLogical, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, Shuffle, 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_InvalidNodeId) +HARDWARE_INTRINSIC(Vector512, Shuffle, 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_SpecialCodeGen) HARDWARE_INTRINSIC(Vector512, Sqrt, 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_InvalidNodeId) -HARDWARE_INTRINSIC(Vector512, Store, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, StoreAligned, 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) -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_InvalidNodeId) 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) 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_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg) @@ -365,7 +275,6 @@ HARDWARE_INTRINSIC(Vector512, WidenUpper, HARDWARE_INTRINSIC(Vector512, WithElement, 64, 3, 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_NoContainment|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, WithLower, 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, WithUpper, 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, Xor, 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_InvalidNodeId) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg EncodesExtraTypeArg Instructions Category Flags diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 8aaee01f4c41c..917c0c9d95641 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -955,6 +955,7 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT // clsHnd -- class handle containing the intrinsic function. // method -- method handle of the intrinsic function. // sig -- signature of the intrinsic call. +// entryPoint -- The entry point information required for R2R scenarios // simdBaseJitType -- generic argument of the intrinsic. // retType -- return type of the intrinsic. // mustExpand -- true if the intrinsic must return a GenTree*; otherwise, false @@ -965,7 +966,7 @@ GenTree* Compiler::impNonConstFallback(NamedIntrinsic intrinsic, var_types simdT GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig R2RARG(CORINFO_CONST_LOOKUP* entryPoint), CorInfoType simdBaseJitType, var_types retType, unsigned simdSize, @@ -986,6 +987,13 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(varTypeIsArithmetic(simdBaseType)); } +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP emptyEntryPoint; + + emptyEntryPoint.addr = nullptr; + emptyEntryPoint.accessType = IAT_VALUE; +#endif // FEATURE_READYTORUN + switch (intrinsic) { case NI_Vector128_Abs: @@ -1003,9 +1011,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Add: - case NI_Vector256_Add: - case NI_Vector512_Add: case NI_Vector128_op_Addition: case NI_Vector256_op_Addition: case NI_Vector512_op_Addition: @@ -1039,59 +1044,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_As: - case NI_Vector128_AsByte: - case NI_Vector128_AsDouble: - case NI_Vector128_AsInt16: - case NI_Vector128_AsInt32: - case NI_Vector128_AsInt64: - case NI_Vector128_AsNInt: - case NI_Vector128_AsNUInt: - case NI_Vector128_AsSByte: - case NI_Vector128_AsSingle: - case NI_Vector128_AsUInt16: - case NI_Vector128_AsUInt32: - case NI_Vector128_AsUInt64: - case NI_Vector128_AsVector4: - case NI_Vector256_As: - case NI_Vector256_AsByte: - case NI_Vector256_AsDouble: - case NI_Vector256_AsInt16: - case NI_Vector256_AsInt32: - case NI_Vector256_AsInt64: - case NI_Vector256_AsNInt: - case NI_Vector256_AsNUInt: - case NI_Vector256_AsSByte: - case NI_Vector256_AsSingle: - case NI_Vector256_AsUInt16: - case NI_Vector256_AsUInt32: - case NI_Vector256_AsUInt64: - case NI_Vector512_As: - case NI_Vector512_AsByte: - case NI_Vector512_AsDouble: - case NI_Vector512_AsInt16: - case NI_Vector512_AsInt32: - case NI_Vector512_AsInt64: - case NI_Vector512_AsNInt: - case NI_Vector512_AsNUInt: - case NI_Vector512_AsSByte: - case NI_Vector512_AsSingle: - case NI_Vector512_AsUInt16: - case NI_Vector512_AsUInt32: - case NI_Vector512_AsUInt64: - { - // We fold away the cast here, as it only exists to satisfy - // the type system. It is safe to do this here since the retNode type - // and the signature return type are both the same TYP_SIMD. - - assert(sig->numArgs == 1); - - retNode = impSIMDPopStack(); - SetOpLclRelatedToSIMDIntrinsic(retNode); - assert(retNode->gtType == getSIMDTypeForSize(getSIMDTypeSizeInBytes(sig->retTypeSigClass))); - break; - } - case NI_Vector128_AsVector: { assert(sig->numArgs == 1); @@ -1100,8 +1052,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (vectorTByteLength == YMM_REGSIZE_BYTES) { // Vector is TYP_SIMD32, so we should treat this as a call to Vector128.ToVector256 - return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, sig, simdBaseJitType, retType, - simdSize, mustExpand); + return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, sig R2RARG(&emptyEntryPoint), + simdBaseJitType, retType, simdSize, mustExpand); } else if (vectorTByteLength == XMM_REGSIZE_BYTES) { @@ -1210,8 +1162,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case TYP_SIMD32: { // Vector is TYP_SIMD32, so we should treat this as a call to Vector256.GetLower - return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize, mustExpand); + return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig R2RARG(&emptyEntryPoint), + simdBaseJitType, retType, simdSize, mustExpand); } default: @@ -1250,14 +1202,15 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector256_AsVector) { - return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize, mustExpand); + return impSpecialIntrinsic(NI_Vector256_GetLower, clsHnd, method, sig R2RARG(&emptyEntryPoint), + simdBaseJitType, retType, simdSize, mustExpand); } else { assert(intrinsic == NI_Vector256_AsVector256); - return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, sig, simdBaseJitType, - retType, 16, mustExpand); + return impSpecialIntrinsic(NI_Vector128_ToVector256, clsHnd, method, + sig R2RARG(&emptyEntryPoint), simdBaseJitType, retType, 16, + mustExpand); } } } @@ -1283,14 +1236,14 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector512_AsVector) { - return impSpecialIntrinsic(NI_Vector512_GetLower, clsHnd, method, sig, simdBaseJitType, retType, - simdSize, mustExpand); + return impSpecialIntrinsic(NI_Vector512_GetLower, clsHnd, method, sig R2RARG(&emptyEntryPoint), + simdBaseJitType, retType, simdSize, mustExpand); } else { assert(intrinsic == NI_Vector512_AsVector512); - return impSpecialIntrinsic(NI_Vector256_ToVector512, clsHnd, method, sig, simdBaseJitType, retType, - 32, mustExpand); + return impSpecialIntrinsic(NI_Vector256_ToVector512, clsHnd, method, sig R2RARG(&emptyEntryPoint), + simdBaseJitType, retType, 32, mustExpand); } break; } @@ -1303,14 +1256,16 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (intrinsic == NI_Vector512_AsVector) { - return impSpecialIntrinsic(NI_Vector512_GetLower128, clsHnd, method, sig, simdBaseJitType, - retType, simdSize, mustExpand); + return impSpecialIntrinsic(NI_Vector512_GetLower128, clsHnd, method, + sig R2RARG(&emptyEntryPoint), simdBaseJitType, retType, simdSize, + mustExpand); } else { assert(intrinsic == NI_Vector512_AsVector512); - return impSpecialIntrinsic(NI_Vector128_ToVector512, clsHnd, method, sig, simdBaseJitType, - retType, 16, mustExpand); + return impSpecialIntrinsic(NI_Vector128_ToVector512, clsHnd, method, + sig R2RARG(&emptyEntryPoint), simdBaseJitType, retType, 16, + mustExpand); } } } @@ -1321,9 +1276,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_BitwiseAnd: - case NI_Vector256_BitwiseAnd: - case NI_Vector512_BitwiseAnd: case NI_Vector128_op_BitwiseAnd: case NI_Vector256_op_BitwiseAnd: case NI_Vector512_op_BitwiseAnd: @@ -1337,9 +1289,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_BitwiseOr: - case NI_Vector256_BitwiseOr: - case NI_Vector512_BitwiseOr: case NI_Vector128_op_BitwiseOr: case NI_Vector256_op_BitwiseOr: case NI_Vector512_op_BitwiseOr: @@ -1893,9 +1842,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Divide: - case NI_Vector256_Divide: - case NI_Vector512_Divide: case NI_Vector128_op_Division: case NI_Vector256_op_Division: case NI_Vector512_op_Division: @@ -1979,9 +1925,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_EqualsAll: - case NI_Vector256_EqualsAll: - case NI_Vector512_EqualsAll: case NI_Vector128_op_Equality: case NI_Vector256_op_Equality: case NI_Vector512_op_Equality: @@ -2225,15 +2168,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_get_Zero: - case NI_Vector256_get_Zero: - case NI_Vector512_get_Zero: - { - assert(sig->numArgs == 0); - retNode = gtNewZeroConNode(retType); - break; - } - case NI_Vector128_GetElement: case NI_Vector256_GetElement: case NI_Vector512_GetElement: @@ -2504,9 +2438,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_AVX_LoadVector256: case NI_AVX512F_LoadVector512: case NI_AVX512BW_LoadVector512: - case NI_Vector128_Load: - case NI_Vector256_Load: - case NI_Vector512_Load: case NI_Vector128_LoadUnsafe: case NI_Vector256_LoadUnsafe: case NI_Vector512_LoadUnsafe: @@ -2609,9 +2540,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Multiply: - case NI_Vector256_Multiply: - case NI_Vector512_Multiply: case NI_Vector128_op_Multiply: case NI_Vector256_op_Multiply: case NI_Vector512_op_Multiply: @@ -2704,9 +2632,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Negate: - case NI_Vector256_Negate: - case NI_Vector512_Negate: case NI_Vector128_op_UnaryNegation: case NI_Vector256_op_UnaryNegation: case NI_Vector512_op_UnaryNegation: @@ -2722,9 +2647,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_OnesComplement: - case NI_Vector256_OnesComplement: - case NI_Vector512_OnesComplement: case NI_Vector128_op_OnesComplement: case NI_Vector256_op_OnesComplement: case NI_Vector512_op_OnesComplement: @@ -2770,18 +2692,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_op_UnaryPlus: - case NI_Vector256_op_UnaryPlus: - case NI_Vector512_op_UnaryPlus: - { - assert(sig->numArgs == 1); - retNode = impSIMDPopStack(); - break; - } - - case NI_Vector128_Subtract: - case NI_Vector256_Subtract: - case NI_Vector512_Subtract: case NI_Vector128_op_Subtraction: case NI_Vector256_op_Subtraction: case NI_Vector512_op_Subtraction: @@ -2799,9 +2709,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_ShiftLeft: - case NI_Vector256_ShiftLeft: - case NI_Vector512_ShiftLeft: case NI_Vector128_op_LeftShift: case NI_Vector256_op_LeftShift: case NI_Vector512_op_LeftShift: @@ -2824,9 +2731,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_ShiftRightArithmetic: - case NI_Vector256_ShiftRightArithmetic: - case NI_Vector512_ShiftRightArithmetic: case NI_Vector128_op_RightShift: case NI_Vector256_op_RightShift: case NI_Vector512_op_RightShift: @@ -2860,9 +2764,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_ShiftRightLogical: - case NI_Vector256_ShiftRightLogical: - case NI_Vector512_ShiftRightLogical: case NI_Vector128_op_UnsignedRightShift: case NI_Vector256_op_UnsignedRightShift: case NI_Vector512_op_UnsignedRightShift: @@ -2888,77 +2789,24 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, GenTree* indices = impStackTop(0).val; - if (!indices->IsVectorConst()) + if (!indices->IsVectorConst() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) { - // TODO-XARCH-CQ: Handling non-constant indices is a bit more complex - break; - } - - size_t elementSize = genTypeSize(simdBaseType); - size_t elementCount = simdSize / elementSize; + assert(sig->numArgs == 2); - if (simdSize == 32) - { - if (!compOpportunisticallyDependsOn(InstructionSet_AVX2)) + if (!opts.OptimizationEnabled()) { - // While we could accelerate some functions on hardware with only AVX support - // it's likely not worth it overall given that IsHardwareAccelerated reports false - break; + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; } - else if ((varTypeIsByte(simdBaseType) && - !compOpportunisticallyDependsOn(InstructionSet_AVX512VBMI_VL)) || - (varTypeIsShort(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX512BW_VL))) - { - bool crossLane = false; - for (size_t index = 0; index < elementCount; index++) - { - uint64_t value = indices->GetIntegralVectorConstElement(index, simdBaseType); - - if (value >= elementCount) - { - continue; - } - - if (index < (elementCount / 2)) - { - if (value >= (elementCount / 2)) - { - crossLane = true; - break; - } - } - else if (value < (elementCount / 2)) - { - crossLane = true; - break; - } - } + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); - if (crossLane) - { - // TODO-XARCH-CQ: We should emulate cross-lane shuffling for byte/sbyte and short/ushort - break; - } - } - } - else if (simdSize == 64) - { - if (varTypeIsByte(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX512VBMI)) - { - // TYP_BYTE, TYP_UBYTE need AVX512VBMI. - break; - } - } - else - { - assert(simdSize == 16); + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize); - if (varTypeIsSmall(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_SSSE3)) - { - // TYP_BYTE, TYP_UBYTE, TYP_SHORT, and TYP_USHORT need SSSE3 to be able to shuffle any operation - break; - } + retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); + break; } if (sig->numArgs == 2) @@ -3009,9 +2857,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Store: - case NI_Vector256_Store: - case NI_Vector512_Store: case NI_Vector128_StoreUnsafe: case NI_Vector256_StoreUnsafe: case NI_Vector512_StoreUnsafe: @@ -3264,9 +3109,21 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (!indexOp->OperIsConst()) { - // TODO-XARCH-CQ: We should always import these like we do with GetElement - // Index is not a constant, use the software fallback - return nullptr; + if (!opts.OptimizationEnabled()) + { + // Only enable late stage rewriting if optimizations are enabled + // as we won't otherwise encounter a constant at the later point + return nullptr; + } + + op3 = impPopStack().val; + op2 = impPopStack().val; + op1 = impSIMDPopStack(); + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, simdBaseJitType, simdSize); + + retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); + break; } ssize_t imm8 = indexOp->AsIntCon()->IconValue(); @@ -3362,9 +3219,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } - case NI_Vector128_Xor: - case NI_Vector256_Xor: - case NI_Vector512_Xor: case NI_Vector128_op_ExclusiveOr: case NI_Vector256_op_ExclusiveOr: case NI_Vector512_op_ExclusiveOr: diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 85bd5d8dfbd16..6c445351f78fe 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -228,9 +228,23 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool isTailCall = canTailCall && (tailCallFlags != 0); +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP entryPoint; + + if (opts.IsReadyToRun() && (callInfo->kind == CORINFO_CALL)) + { + entryPoint = callInfo->codePointerLookup.constLookup; + } + else + { + entryPoint.addr = nullptr; + entryPoint.accessType = IAT_VALUE; + } +#endif // FEATURE_READYTORUN + call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken, isReadonlyCall, isTailCall, - opcode == CEE_CALLVIRT, pConstrainedResolvedToken, callInfo->thisTransform, &ni, - &isSpecialIntrinsic); + opcode == CEE_CALLVIRT, pConstrainedResolvedToken, + callInfo->thisTransform R2RARG(&entryPoint), &ni, &isSpecialIntrinsic); if (compDonotInline()) { @@ -239,22 +253,6 @@ var_types Compiler::impImportCall(OPCODE opcode, if (call != nullptr) { -#ifdef FEATURE_READYTORUN - if (call->OperGet() == GT_INTRINSIC) - { - if (opts.IsReadyToRun()) - { - noway_assert(callInfo->kind == CORINFO_CALL); - call->AsIntrinsic()->gtEntryPoint = callInfo->codePointerLookup.constLookup; - } - else - { - call->AsIntrinsic()->gtEntryPoint.addr = nullptr; - call->AsIntrinsic()->gtEntryPoint.accessType = IAT_VALUE; - } - } -#endif - bIntrinsicImported = true; goto DONE_CALL; } @@ -2831,6 +2829,7 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) // pConstrainedResolvedToken -- resolved token for constrained call, or nullptr // if call is not constrained // constraintCallThisTransform -- this transform to apply for a constrained call +// entryPoint - The entry point information required for R2R scenarios // pIntrinsicName [OUT] -- intrinsic name (see enumeration in namedintrinsiclist.h) // for "traditional" jit intrinsics // isSpecialIntrinsic [OUT] -- set true if intrinsic expansion is a call @@ -2872,9 +2871,10 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, bool tailCall, bool callvirt, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, - CORINFO_THIS_TRANSFORM constraintCallThisTransform, - NamedIntrinsic* pIntrinsicName, - bool* isSpecialIntrinsic) + CORINFO_THIS_TRANSFORM constraintCallThisTransform + R2RARG(CORINFO_CONST_LOOKUP* entryPoint), + NamedIntrinsic* pIntrinsicName, + bool* isSpecialIntrinsic) { bool mustExpand = false; bool isSpecial = false; @@ -3049,15 +3049,15 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, #if defined(TARGET_XARCH) // We can't guarantee that all overloads for the xplat intrinsics can be // handled by the AltJit, so limit only the platform specific intrinsics - assert((NI_Vector512_Xor + 1) == NI_X86Base_BitScanForward); + assert((NI_Vector512_WithUpper + 1) == NI_X86Base_BitScanForward); - if (ni < NI_Vector512_Xor) + if (ni < NI_Vector512_WithUpper) #elif defined(TARGET_ARM64) // We can't guarantee that all overloads for the xplat intrinsics can be // handled by the AltJit, so limit only the platform specific intrinsics - assert((NI_Vector128_Xor + 1) == NI_AdvSimd_Abs); + assert((NI_Vector128_WithUpper + 1) == NI_AdvSimd_Abs); - if (ni < NI_Vector128_Xor) + if (ni < NI_Vector128_WithUpper) #else #error Unsupported platform #endif @@ -3068,7 +3068,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, } } - GenTree* hwintrinsic = impHWIntrinsic(ni, clsHnd, method, sig, mustExpand); + GenTree* hwintrinsic = impHWIntrinsic(ni, clsHnd, method, sig R2RARG(entryPoint), mustExpand); if (mustExpand && (hwintrinsic == nullptr)) { @@ -3089,6 +3089,13 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, } #endif // FEATURE_HW_INTRINSICS + if ((ni == NI_System_Numerics_Intrinsic) || (ni == NI_System_Runtime_Intrinsics_Intrinsic)) + { + // These are special markers used just to ensure we still get the inlining profitability + // boost. We actually have the implementation in managed, however, to keep the JIT simpler. + return nullptr; + } + if (!isIntrinsic) { // Outside the cases above, there are many intrinsics which apply to only a @@ -3355,13 +3362,26 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, else { // op1 is not a known constant, we'll do the expansion in morph - retNode = new (this, GT_INTRINSIC) GenTreeIntrinsic(TYP_INT, op1, ni, method); + retNode = new (this, GT_INTRINSIC) GenTreeIntrinsic(TYP_INT, op1, ni, method R2RARG(*entryPoint)); JITDUMP("\nConverting RuntimeHelpers.IsKnownConstant to:\n"); DISPTREE(retNode); } break; } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences: + { + assert(sig->sigInst.methInstCount == 1); + + CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; + ClassLayout* fromLayout = nullptr; + var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); + + bool refOrContains = varTypeIsGC(fromType) || (fromLayout != nullptr && fromLayout->HasGCPtr()); + retNode = refOrContains ? gtNewIconNode(1) : gtNewIconNode(0); + break; + } + case NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference: { assert(sig->numArgs == 1); @@ -4063,7 +4083,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Math_Log2: case NI_System_Math_Log10: { - retNode = impMathIntrinsic(method, sig, callType, ni, tailCall); + retNode = impMathIntrinsic(method, sig R2RARG(entryPoint), callType, ni, tailCall); break; } @@ -4156,7 +4176,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Math_Tanh: case NI_System_Math_Truncate: { - retNode = impMathIntrinsic(method, sig, callType, ni, tailCall); + retNode = impMathIntrinsic(method, sig R2RARG(entryPoint), callType, ni, tailCall); break; } @@ -4252,7 +4272,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, { JITDUMP("Expanding as special intrinsic\n"); impPopStack(); - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, ni, method); + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, ni, method R2RARG(*entryPoint)); // Set the CALL flag to indicate that the operator is implemented by a call. // Set also the EXCEPTION flag because the native implementation of @@ -9034,8 +9055,16 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, if (intrinsicName == NI_System_Math_ReciprocalSqrtEstimate) { assert(!IsIntrinsicImplementedByUserCall(NI_System_Math_Sqrt)); + +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP entryPoint; + + entryPoint.addr = nullptr; + entryPoint.accessType = IAT_VALUE; +#endif // FEATURE_READYTORUN + op1 = new (this, GT_INTRINSIC) - GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, nullptr); + GenTreeIntrinsic(genActualType(callType), op1, NI_System_Math_Sqrt, nullptr R2RARG(entryPoint)); } return gtNewOperNode(GT_DIV, genActualType(callType), gtNewDconNode(1.0, callType), op1); } @@ -9048,7 +9077,7 @@ GenTree* Compiler::impEstimateIntrinsic(CORINFO_METHOD_HANDLE method, } GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, - CORINFO_SIG_INFO* sig, + CORINFO_SIG_INFO* sig R2RARG(CORINFO_CONST_LOOKUP* entryPoint), var_types callType, NamedIntrinsic intrinsicName, bool tailCall) @@ -9085,7 +9114,8 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op1 = impPopStack().val; op1 = impImplicitR4orR8Cast(op1, callType); - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicName, method); + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, intrinsicName, method R2RARG(*entryPoint)); break; case 2: @@ -9097,8 +9127,8 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op1 = impPopStack().val; op1 = impImplicitR4orR8Cast(op1, callType); op2 = impImplicitR4orR8Cast(op2, callType); - op1 = - new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicName, method); + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicName, method R2RARG(*entryPoint)); break; default: @@ -10070,6 +10100,12 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_Throw_PlatformNotSupportedException; } + else + { + // Otherwise mark this as a general intrinsic in the namespace + // so we can still get the inlining profitability boost. + result = NI_System_Runtime_Intrinsics_Intrinsic; + } } } } @@ -10093,6 +10129,11 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant; } + else if (strcmp(methodName, "IsReferenceOrContainsReferences") == 0) + { + result = + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences; + } } else if (strcmp(className, "Unsafe") == 0) { @@ -10294,6 +10335,12 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_Throw_PlatformNotSupportedException; } + else + { + // Otherwise mark this as a general intrinsic in the namespace + // so we can still get the inlining profitability boost. + result = NI_System_Numerics_Intrinsic; + } } } } diff --git a/src/coreclr/jit/inductionvariableopts.cpp b/src/coreclr/jit/inductionvariableopts.cpp index 019067f9d6e8f..e4b03b49905a0 100644 --- a/src/coreclr/jit/inductionvariableopts.cpp +++ b/src/coreclr/jit/inductionvariableopts.cpp @@ -861,6 +861,286 @@ bool Compiler::optWidenPrimaryIV(FlowGraphNaturalLoop* loop, return true; } +//------------------------------------------------------------------------ +// optMakeLoopDownwardsCounted: Transform a loop to be downwards counted if +// profitable and legal. +// +// Parameters: +// scevContext - Context for scalar evolution +// loop - Loop to transform +// loopLocals - Data structure that tracks occurrences of locals in the loop +// +// Returns: +// True if the loop was made downwards counted; otherwise false. +// +bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, + FlowGraphNaturalLoop* loop, + LoopLocalOccurrences* loopLocals) +{ + JITDUMP("Checking if we should make " FMT_LP " downwards counted\n", loop->GetIndex()); + + if (loop->ExitEdges().size() != 1) + { + // With multiple exits we generally can only compute an upper bound on + // the backedge count. + JITDUMP(" No; has multiple exits\n"); + return false; + } + + BasicBlock* exiting = loop->ExitEdge(0)->getSourceBlock(); + if (!exiting->KindIs(BBJ_COND)) + { + JITDUMP(" No; exit is not BBJ_COND\n"); + return false; + } + + Statement* jtrueStmt = exiting->lastStmt(); + GenTree* jtrue = jtrueStmt->GetRootNode(); + assert(jtrue->OperIs(GT_JTRUE)); + GenTree* cond = jtrue->gtGetOp1(); + + if ((jtrue->gtFlags & GTF_SIDE_EFFECT) != 0) + { + // If the IV is used as part of the side effect then we can't + // transform; otherwise we could. TODO-CQ: Make this determination and + // extract side effects from the jtrue to make this work. + JITDUMP(" No; exit node has side effects\n"); + return false; + } + + bool checkProfitability = !compStressCompile(STRESS_DOWNWARDS_COUNTED_LOOPS, 50); + + if (checkProfitability && cond->OperIsCompare() && + (cond->gtGetOp1()->IsIntegralConst(0) || cond->gtGetOp2()->IsIntegralConst(0))) + { + JITDUMP(" No; operand of condition [%06u] is already 0\n", dspTreeID(cond)); + return false; + } + + // Making a loop downwards counted is profitable if there is a primary IV + // that has no uses outside the loop test (and mutating itself). Check that + // now. + ArrayStack removableLocals(getAllocator(CMK_LoopOpt)); + + for (Statement* stmt : loop->GetHeader()->Statements()) + { + if (!stmt->IsPhiDefnStmt()) + { + break; + } + + unsigned candidateLclNum = stmt->GetRootNode()->AsLclVarCommon()->GetLclNum(); + LclVarDsc* candidateVarDsc = lvaGetDesc(candidateLclNum); + if (candidateVarDsc->lvIsStructField && loopLocals->HasAnyOccurrences(loop, candidateVarDsc->lvParentLcl)) + { + continue; + } + + if (candidateVarDsc->lvDoNotEnregister) + { + // This filters out locals that may be live into exceptional exits. + continue; + } + + BasicBlockVisit visitResult = loop->VisitRegularExitBlocks([=](BasicBlock* block) { + if (VarSetOps::IsMember(this, block->bbLiveIn, candidateVarDsc->lvVarIndex)) + { + return BasicBlockVisit::Abort; + } + + return BasicBlockVisit::Continue; + }); + + if (visitResult == BasicBlockVisit::Abort) + { + // Live into an exit. + // TODO-CQ: In some cases it may be profitable to materialize the final value after the loop. + // This requires analysis on whether the required expressions are available there + // (and whether it doesn't extend their lifetimes too much). + continue; + } + + bool hasUseInTest = false; + auto checkRemovableUse = [=, &hasUseInTest](BasicBlock* block, Statement* stmt) { + if (stmt == jtrueStmt) + { + hasUseInTest = true; + // Use is inside the loop test that has no side effects (as we checked above), can remove + return true; + } + + GenTree* rootNode = stmt->GetRootNode(); + if (!rootNode->OperIsLocalStore()) + { + // Cannot reason about this use of the local, cannot remove + // TODO-CQ: In some cases it may be profitable to compute the + // value in terms of the down-counting IV. + return false; + } + + if (rootNode->AsLclVarCommon()->GetLclNum() != candidateLclNum) + { + // Used to compute a value stored to some other local, cannot remove + return false; + } + + if ((rootNode->AsLclVarCommon()->Data()->gtFlags & GTF_SIDE_EFFECT) != 0) + { + // May be used inside the data node for something that has side effects, cannot remove + return false; + } + + // Can remove this store + return true; + }; + + if (!loopLocals->VisitStatementsWithOccurrences(loop, candidateLclNum, checkRemovableUse)) + { + // Aborted means we found a non-removable use + continue; + } + + if (!hasUseInTest) + { + // This one we can remove, but we expect it to be removable even without this transformation. + continue; + } + + JITDUMP(" Expecting to be able to remove V%02u by making this loop reverse counted\n", candidateLclNum); + removableLocals.Push(candidateLclNum); + } + + if (checkProfitability && (removableLocals.Height() <= 0)) + { + JITDUMP(" Found no potentially removable locals when making this loop downwards counted\n"); + return false; + } + + // Commonly there is only a single shared exit and backedge. In that case + // we do not even need to compute dominators since all backedges are + // definitely dominated by the exit. + if ((loop->BackEdges().size() == 1) && loop->BackEdge(0)->getSourceBlock() == loop->ExitEdge(0)->getSourceBlock()) + { + // Exit definitely dominates the latch since they are the same block. + // Fall through. + JITDUMP(" Loop exit is also the only backedge\n"); + } + else + { + for (FlowEdge* backedge : loop->BackEdges()) + { + // We know dom(x, y) => ancestor(x, y), so we can utilize the + // contrapositive !ancestor(x, y) => !dom(x, y) to avoid computing + // dominators in some cases. + if (!m_dfsTree->IsAncestor(exiting, backedge->getSourceBlock())) + { + JITDUMP(" No; exiting block " FMT_BB " is not an ancestor of backedge source " FMT_BB "\n", + exiting->bbNum, backedge->getSourceBlock()->bbNum); + return false; + } + + if (m_domTree == nullptr) + { + m_domTree = FlowGraphDominatorTree::Build(m_dfsTree); + } + + if (!m_domTree->Dominates(exiting, backedge->getSourceBlock())) + { + JITDUMP(" No; exiting block " FMT_BB " does not dominate backedge source " FMT_BB "\n", exiting->bbNum, + backedge->getSourceBlock()->bbNum); + return false; + } + } + } + + // At this point we know that the single exit dominates all backedges. + JITDUMP(" All backedges are dominated by exiting block " FMT_BB "\n", exiting->bbNum); + + Scev* backedgeCount = scevContext.ComputeExitNotTakenCount(exiting); + if (backedgeCount == nullptr) + { + JITDUMP(" Could not compute backedge count -- not a counted loop\n"); + return false; + } + + BasicBlock* preheader = loop->GetPreheader(); + assert(preheader != nullptr); + + // We are interested in phrasing the test as (--x == 0). That requires us + // to add one to the computed backedge count, giving us the trip count of + // the loop. We do not need to worry about overflow here (even with + // wraparound we have the right behavior). + Scev* tripCount = scevContext.Simplify( + scevContext.NewBinop(ScevOper::Add, backedgeCount, scevContext.NewConstant(backedgeCount->Type, 1))); + GenTree* tripCountNode = scevContext.Materialize(tripCount); + if (tripCountNode == nullptr) + { + JITDUMP(" Could not materialize trip count into IR\n"); + return false; + } + + JITDUMP(" Converting " FMT_LP " into a downwards loop\n", loop->GetIndex()); + + unsigned tripCountLcl = lvaGrabTemp(false DEBUGARG("Trip count IV")); + GenTree* store = gtNewTempStore(tripCountLcl, tripCountNode); + + Statement* newStmt = fgNewStmtFromTree(store); + fgInsertStmtAtEnd(preheader, newStmt); + + JITDUMP(" Inserted initialization of tripcount local\n\n"); + DISPSTMT(newStmt); + + genTreeOps exitOp = GT_EQ; + if (loop->ContainsBlock(exiting->GetTrueTarget())) + { + exitOp = GT_NE; + } + + GenTree* negOne = tripCount->TypeIs(TYP_LONG) ? gtNewLconNode(-1) : gtNewIconNode(-1, tripCount->Type); + GenTree* decremented = + gtNewOperNode(GT_ADD, tripCount->Type, gtNewLclVarNode(tripCountLcl, tripCount->Type), negOne); + + store = gtNewTempStore(tripCountLcl, decremented); + + newStmt = fgNewStmtFromTree(store); + fgInsertStmtNearEnd(exiting, newStmt); + + JITDUMP("\n Inserted decrement of tripcount local\n\n"); + DISPSTMT(newStmt); + + // Update the test. + cond->SetOper(exitOp); + cond->AsOp()->gtOp1 = gtNewLclVarNode(tripCountLcl, tripCount->Type); + cond->AsOp()->gtOp2 = gtNewZeroConNode(tripCount->Type); + + gtSetStmtInfo(jtrueStmt); + fgSetStmtSeq(jtrueStmt); + + JITDUMP("\n Updated exit test:\n"); + DISPSTMT(jtrueStmt); + + JITDUMP("\n Now removing uses of old IVs\n"); + + for (int i = 0; i < removableLocals.Height(); i++) + { + unsigned removableLcl = removableLocals.Bottom(i); + JITDUMP(" Removing uses of V%02u\n", removableLcl); + auto deleteStatement = [=](BasicBlock* block, Statement* stmt) { + if (stmt != jtrueStmt) + { + fgRemoveStmt(block, stmt); + } + + return true; + }; + + loopLocals->VisitStatementsWithOccurrences(loop, removableLcl, deleteStatement); + } + + JITDUMP("\n"); + return true; +} + //------------------------------------------------------------------------ // optInductionVariables: Try and optimize induction variables in the method. // @@ -889,12 +1169,9 @@ PhaseStatus Compiler::optInductionVariables() bool changed = false; - // Currently we only do IV widening which generally is only profitable for - // x64 because arm64 addressing modes can include the zero/sign-extension - // of the index for free. -#if defined(TARGET_XARCH) && defined(TARGET_64BIT) - m_dfsTree = fgComputeDfs(); - m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + optReachableBitVecTraits = nullptr; + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); LoopLocalOccurrences loopLocals(m_loops); @@ -907,6 +1184,25 @@ PhaseStatus Compiler::optInductionVariables() DBEXEC(verbose, FlowGraphNaturalLoop::Dump(loop)); scevContext.ResetForLoop(loop); + // We may not have preheaders here since RBO/assertion prop may have changed + // the flow graph + BasicBlock* preheader = loop->GetPreheader(); + if (preheader == nullptr) + { + JITDUMP(" No preheader; skipping\n"); + continue; + } + + if (optMakeLoopDownwardsCounted(scevContext, loop, &loopLocals)) + { + Metrics.LoopsMadeDownwardsCounted++; + changed = true; + } + + // IV widening is generally only profitable for x64 because arm64 + // addressing modes can include the zero/sign-extension of the index + // for free. +#if defined(TARGET_XARCH) && defined(TARGET_64BIT) int numWidened = 0; for (Statement* stmt : loop->GetHeader()->Statements()) @@ -964,10 +1260,10 @@ PhaseStatus Compiler::optInductionVariables() { Metrics.LoopsIVWidened++; } +#endif } fgInvalidateDfsTree(); -#endif return changed ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } diff --git a/src/coreclr/jit/instrsriscv64.h b/src/coreclr/jit/instrsriscv64.h index 281dacaeec5dc..b1f690b3088b7 100644 --- a/src/coreclr/jit/instrsriscv64.h +++ b/src/coreclr/jit/instrsriscv64.h @@ -35,7 +35,7 @@ INST(invalid, "INVALID", 0, BAD_CODE) INST(nop, "nop", 0, 0x00000013) //// R_R -INST(mov, "mov", 0, 0x00000013) +INST(mov, "mv", 0, 0x00000013) INST(sext_w, "sext.w", 0, 0x0000001b) ////R_I diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index e354e3421acbe..dbd56791d88d8 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -567,6 +567,7 @@ OPT_CONFIG_STRING(JitOnlyOptimizeRange, OPT_CONFIG_STRING(JitEnablePhysicalPromotionRange, W("JitEnablePhysicalPromotionRange")) OPT_CONFIG_STRING(JitEnableCrossBlockLocalAssertionPropRange, W("JitEnableCrossBlockLocalAssertionPropRange")) OPT_CONFIG_STRING(JitEnableInductionVariableOptsRange, W("JitEnableInductionVariableOptsRange")) +OPT_CONFIG_STRING(JitEnableLocalAddrPropagationRange, W("JitEnableLocalAddrPropagationRange")) OPT_CONFIG_INTEGER(JitDoSsa, W("JitDoSsa"), 1) // Perform Static Single Assignment (SSA) numbering on the variables OPT_CONFIG_INTEGER(JitDoValueNumber, W("JitDoValueNumber"), 1) // Perform value numbering on method expressions diff --git a/src/coreclr/jit/jitmetadatalist.h b/src/coreclr/jit/jitmetadatalist.h index e077f9f57b255..209133f4324b0 100644 --- a/src/coreclr/jit/jitmetadatalist.h +++ b/src/coreclr/jit/jitmetadatalist.h @@ -35,6 +35,7 @@ JITMETADATAMETRIC(LoopAlignmentCandidates, int, 0) JITMETADATAMETRIC(LoopsAligned, int, 0) JITMETADATAMETRIC(LoopsIVWidened, int, 0) JITMETADATAMETRIC(WidenedIVs, int, 0) +JITMETADATAMETRIC(LoopsMadeDownwardsCounted, int, 0) JITMETADATAMETRIC(VarsInSsa, int, 0) JITMETADATAMETRIC(HoistedExpressions, int, 0) JITMETADATAMETRIC(RedundantBranchesEliminated, int, JIT_METADATA_HIGHER_IS_BETTER) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 6a4534fc3f63a..b8ecc8b1f736f 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -175,6 +175,277 @@ void Compiler::fgSequenceLocals(Statement* stmt) seq.Sequence(stmt); } +struct LocalEqualsLocalAddrAssertion +{ + // Local num on the LHS + unsigned DestLclNum; + // Local num on the RHS (having its adress taken) + unsigned AddressLclNum; + // Offset into RHS local + unsigned AddressOffset; + + LocalEqualsLocalAddrAssertion(unsigned destLclNum, unsigned addressLclNum, unsigned addressOffset) + : DestLclNum(destLclNum) + , AddressLclNum(addressLclNum) + , AddressOffset(addressOffset) + { + } + +#ifdef DEBUG + void Print() const + { + printf("V%02u = &V%02u[+%03u]", DestLclNum, AddressLclNum, AddressOffset); + } +#endif +}; + +struct AssertionKeyFuncs +{ + static bool Equals(const LocalEqualsLocalAddrAssertion& lhs, const LocalEqualsLocalAddrAssertion rhs) + { + return (lhs.DestLclNum == rhs.DestLclNum) && (lhs.AddressLclNum == rhs.AddressLclNum) && + (lhs.AddressOffset == rhs.AddressOffset); + } + + static unsigned GetHashCode(const LocalEqualsLocalAddrAssertion& val) + { + unsigned hash = val.DestLclNum; + hash ^= val.AddressLclNum + 0x9e3779b9 + (hash << 19) + (hash >> 13); + hash ^= val.AddressOffset + 0x9e3779b9 + (hash << 19) + (hash >> 13); + return hash; + } +}; + +typedef JitHashTable AssertionToIndexMap; + +class LocalEqualsLocalAddrAssertions +{ + Compiler* m_comp; + ArrayStack m_assertions; + AssertionToIndexMap m_map; + uint64_t* m_lclAssertions; + uint64_t* m_outgoingAssertions; + uint64_t m_currentAssertions = 0; + BitVec m_localsToExpose; + +public: + LocalEqualsLocalAddrAssertions(Compiler* comp) + : m_comp(comp) + , m_assertions(comp->getAllocator(CMK_LocalAddressVisitor)) + , m_map(comp->getAllocator(CMK_LocalAddressVisitor)) + { + m_lclAssertions = + comp->lvaCount == 0 ? nullptr : new (comp, CMK_LocalAddressVisitor) uint64_t[comp->lvaCount]{}; + m_outgoingAssertions = new (comp, CMK_LocalAddressVisitor) uint64_t[comp->m_dfsTree->GetPostOrderCount()]{}; + + BitVecTraits localsTraits(comp->lvaCount, comp); + m_localsToExpose = BitVecOps::MakeEmpty(&localsTraits); + } + + //------------------------------------------------------------------------ + // GetLocalsToExpose: Get the bit vector of locals that were marked to be + // exposed. + // + BitVec_ValRet_T GetLocalsToExpose() + { + return m_localsToExpose; + } + + //------------------------------------------------------------------------ + // IsMarkedForExposure: Check if a specific local is marked to be exposed. + // + // Return Value: + // True if so. + // + bool IsMarkedForExposure(unsigned lclNum) + { + BitVecTraits traits(m_comp->lvaCount, m_comp); + if (BitVecOps::IsMember(&traits, m_localsToExpose, lclNum)) + { + return true; + } + + LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum); + if (dsc->lvIsStructField && BitVecOps::IsMember(&traits, m_localsToExpose, dsc->lvParentLcl)) + { + return true; + } + + return false; + } + + //------------------------------------------------------------------- + // StartBlock: Start a new block by computing incoming assertions for the + // block. + // + // Arguments: + // block - The block + // + void StartBlock(BasicBlock* block) + { + if ((m_assertions.Height() == 0) || (block->bbPreds == nullptr) || m_comp->bbIsHandlerBeg(block)) + { + m_currentAssertions = 0; + return; + } + + m_currentAssertions = UINT64_MAX; + for (BasicBlock* pred : block->PredBlocks()) + { + assert(m_comp->m_dfsTree->Contains(pred)); + if (pred->bbPostorderNum <= block->bbPostorderNum) + { + m_currentAssertions = 0; + break; + } + + m_currentAssertions &= m_outgoingAssertions[pred->bbPostorderNum]; + } + +#ifdef DEBUG + if (m_currentAssertions != 0) + { + JITDUMP(FMT_BB " incoming assertions:\n", block->bbNum); + uint64_t assertions = m_currentAssertions; + do + { + uint32_t index = BitOperations::BitScanForward(assertions); + JITDUMP(" A%02u: ", index); + DBEXEC(VERBOSE, m_assertions.BottomRef((int)index).Print()); + JITDUMP("\n"); + + assertions ^= uint64_t(1) << index; + } while (assertions != 0); + } +#endif + } + + //------------------------------------------------------------------- + // EndBlock: End a block by recording its final outgoing assertions. + // + // Arguments: + // block - The block + // + void EndBlock(BasicBlock* block) + { + m_outgoingAssertions[block->bbPostorderNum] = m_currentAssertions; + } + + //------------------------------------------------------------------- + // OnExposed: Mark that a local is having its address taken. + // + // Arguments: + // lclNum - The local + // + void OnExposed(unsigned lclNum) + { + BitVecTraits localsTraits(m_comp->lvaCount, m_comp); + BitVecOps::AddElemD(&localsTraits, m_localsToExpose, lclNum); + } + + //------------------------------------------------------------------- + // Record: Record an assertion about the specified local. + // + // Arguments: + // dstLclNum - Destination local + // srcLclNum - Local having its address taken + // srcOffs - Offset into the source local of the address being taken + // + void Record(unsigned dstLclNum, unsigned srcLclNum, unsigned srcOffs) + { + LocalEqualsLocalAddrAssertion assertion(dstLclNum, srcLclNum, srcOffs); + + unsigned index; + if (m_assertions.Height() >= 64) + { + if (!m_map.Lookup(assertion, &index)) + { + JITDUMP("Out of assertion space; dropping assertion "); + DBEXEC(VERBOSE, assertion.Print()); + JITDUMP("\n"); + return; + } + } + else + { + unsigned* pIndex = m_map.LookupPointerOrAdd(assertion, UINT_MAX); + if (*pIndex == UINT_MAX) + { + index = (unsigned)m_assertions.Height(); + *pIndex = index; + m_assertions.Push(assertion); + m_lclAssertions[dstLclNum] |= uint64_t(1) << index; + + JITDUMP("Adding new assertion A%02u ", index); + DBEXEC(VERBOSE, assertion.Print()); + JITDUMP("\n"); + } + else + { + index = *pIndex; + JITDUMP("Adding existing assertion A%02u ", index); + DBEXEC(VERBOSE, assertion.Print()); + JITDUMP("\n"); + } + } + + m_currentAssertions |= uint64_t(1) << index; + } + + //------------------------------------------------------------------- + // Clear: Clear active assertions about the specified local. + // + // Arguments: + // dstLclNum - Destination local + // + void Clear(unsigned dstLclNum) + { + m_currentAssertions &= ~m_lclAssertions[dstLclNum]; + } + + //----------------------------------------------------------------------------------- + // GetCurrentAssertion: + // Get the current assertion about a local's value. + // + // Arguments: + // lclNum - The local + // + // Return Value: + // Assertion, or nullptr if there is no current assertion. + // + const LocalEqualsLocalAddrAssertion* GetCurrentAssertion(unsigned lclNum) + { + uint64_t curAssertion = m_currentAssertions & m_lclAssertions[lclNum]; + assert(genMaxOneBit(curAssertion)); + if (curAssertion == 0) + { + return nullptr; + } + + return &m_assertions.BottomRef(BitOperations::BitScanForward(curAssertion)); + } + + //----------------------------------------------------------------------------------- + // GetLocalsWithAssertions: + // Get a bit vector of all locals that have assertions about their value. + // + // Return Value: + // Bit vector of locals. + // + BitVec_ValRet_T GetLocalsWithAssertions() + { + BitVecTraits localsTraits(m_comp->lvaCount, m_comp); + BitVec result(BitVecOps::MakeEmpty(&localsTraits)); + + for (int i = 0; i < m_assertions.Height(); i++) + { + BitVecOps::AddElemD(&localsTraits, result, m_assertions.BottomRef(i).DestLclNum); + } + + return result; + } +}; + class LocalAddressVisitor final : public GenTreeVisitor { // During tree traversal every GenTree node produces a "value" that represents: @@ -259,6 +530,13 @@ class LocalAddressVisitor final : public GenTreeVisitor m_offset = lclAddr->GetLclOffs(); } + void Address(unsigned lclNum, unsigned lclOffs) + { + assert(IsUnknown()); + m_lclNum = lclNum; + m_offset = lclOffs; + } + //------------------------------------------------------------------------ // AddOffset: Produce an address value from an address value. // @@ -326,10 +604,12 @@ class LocalAddressVisitor final : public GenTreeVisitor LclFld }; - ArrayStack m_valueStack; - bool m_stmtModified; - bool m_madeChanges; - LocalSequencer* m_sequencer; + ArrayStack m_valueStack; + bool m_stmtModified = false; + bool m_madeChanges = false; + bool m_propagatedAddrs = false; + LocalSequencer* m_sequencer; + LocalEqualsLocalAddrAssertions* m_lclAddrAssertions; public: enum @@ -339,12 +619,11 @@ class LocalAddressVisitor final : public GenTreeVisitor UseExecutionOrder = true, }; - LocalAddressVisitor(Compiler* comp, LocalSequencer* sequencer) + LocalAddressVisitor(Compiler* comp, LocalSequencer* sequencer, LocalEqualsLocalAddrAssertions* assertions) : GenTreeVisitor(comp) , m_valueStack(comp->getAllocator(CMK_LocalAddressVisitor)) - , m_stmtModified(false) - , m_madeChanges(false) , m_sequencer(sequencer) + , m_lclAddrAssertions(assertions) { } @@ -353,6 +632,11 @@ class LocalAddressVisitor final : public GenTreeVisitor return m_madeChanges; } + bool PropagatedAnyAddresses() const + { + return m_propagatedAddrs; + } + void VisitStmt(Statement* stmt) { #ifdef DEBUG @@ -407,6 +691,46 @@ class LocalAddressVisitor final : public GenTreeVisitor #endif // DEBUG } + void VisitBlock(BasicBlock* block) + { + // Make the current basic block address available globally + m_compiler->compCurBB = block; + + if (m_lclAddrAssertions != nullptr) + { + m_lclAddrAssertions->StartBlock(block); + } + + for (Statement* const stmt : block->Statements()) + { +#ifdef FEATURE_SIMD + if (m_compiler->opts.OptimizationEnabled() && stmt->GetRootNode()->TypeIs(TYP_FLOAT) && + stmt->GetRootNode()->OperIsStore()) + { + m_madeChanges |= m_compiler->fgMorphCombineSIMDFieldStores(block, stmt); + } +#endif + + VisitStmt(stmt); + } + + // We could check for GT_JMP inside the visitor, but this node is very + // rare so keeping it here avoids pessimizing the hot code. + if (block->endsWithJmpMethod(m_compiler)) + { + // GT_JMP has implicit uses of all arguments. + for (unsigned lclNum = 0; lclNum < m_compiler->info.compArgsCount; lclNum++) + { + UpdateEarlyRefCount(lclNum, nullptr, nullptr); + } + } + + if (m_lclAddrAssertions != nullptr) + { + m_lclAddrAssertions->EndBlock(block); + } + } + // Morph promoted struct fields and count local occurrences. // // Also create and push the value produced by the visited node. This is done here @@ -499,12 +823,29 @@ class LocalAddressVisitor final : public GenTreeVisitor FALLTHROUGH; case GT_STORE_LCL_VAR: + { assert(TopValue(0).Node() == node->AsLclVarCommon()->Data()); + if (m_lclAddrAssertions != nullptr) + { + HandleLocalStoreAssertions(node->AsLclVarCommon(), TopValue(0)); + } + EscapeValue(TopValue(0), node); PopValue(); - FALLTHROUGH; + + SequenceLocal(node->AsLclVarCommon()); + break; + } case GT_LCL_VAR: + if (m_lclAddrAssertions != nullptr) + { + HandleLocalAssertions(node->AsLclVarCommon(), TopValue(0)); + } + + SequenceLocal(node->AsLclVarCommon()); + break; + case GT_LCL_FLD: SequenceLocal(node->AsLclVarCommon()); break; @@ -589,10 +930,34 @@ class LocalAddressVisitor final : public GenTreeVisitor case GT_STOREIND: case GT_STORE_BLK: + { + assert(TopValue(2).Node() == node); + assert(TopValue(1).Node() == node->AsIndir()->Addr()); assert(TopValue(0).Node() == node->AsIndir()->Data()); + + // Data value always escapes. EscapeValue(TopValue(0), node); + + if (node->AsIndir()->IsVolatile() || !TopValue(1).IsAddress()) + { + // Volatile indirections must not be removed so the address, if any, must be escaped. + EscapeValue(TopValue(1), node); + } + else + { + // This consumes the address. + ProcessIndirection(use, TopValue(1), user); + + if ((m_lclAddrAssertions != nullptr) && (*use)->OperIsLocalStore()) + { + HandleLocalStoreAssertions((*use)->AsLclVarCommon(), TopValue(0)); + } + } + PopValue(); - FALLTHROUGH; + PopValue(); + break; + } case GT_BLK: case GT_IND: @@ -607,6 +972,11 @@ class LocalAddressVisitor final : public GenTreeVisitor else { ProcessIndirection(use, TopValue(0), user); + + if ((m_lclAddrAssertions != nullptr) && (*use)->OperIs(GT_LCL_VAR)) + { + HandleLocalAssertions((*use)->AsLclVarCommon(), TopValue(1)); + } } PopValue(); @@ -754,8 +1124,16 @@ class LocalAddressVisitor final : public GenTreeVisitor if (!hasHiddenStructArg) { - m_compiler->lvaSetVarAddrExposed( - varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS)); + unsigned exposedLclNum = varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum; + + if (m_lclAddrAssertions != nullptr) + { + m_lclAddrAssertions->OnExposed(exposedLclNum); + } + else + { + m_compiler->lvaSetVarAddrExposed(exposedLclNum DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS)); + } } #ifdef TARGET_64BIT @@ -831,8 +1209,15 @@ class LocalAddressVisitor final : public GenTreeVisitor if (isWide) { - m_compiler->lvaSetVarAddrExposed( - varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum DEBUGARG(AddressExposedReason::WIDE_INDIR)); + unsigned exposedLclNum = varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum; + if (m_lclAddrAssertions != nullptr) + { + m_lclAddrAssertions->OnExposed(exposedLclNum); + } + else + { + m_compiler->lvaSetVarAddrExposed(exposedLclNum DEBUGARG(AddressExposedReason::WIDE_INDIR)); + } MorphLocalAddress(node->AsIndir()->Addr(), lclNum, offset); node->gtFlags |= GTF_GLOB_REF; // GLOB_REF may not be set already in the "large offset" case. @@ -857,7 +1242,9 @@ class LocalAddressVisitor final : public GenTreeVisitor void MorphLocalAddress(GenTree* addr, unsigned lclNum, unsigned offset) { assert(addr->TypeIs(TYP_BYREF, TYP_I_IMPL)); - assert(m_compiler->lvaVarAddrExposed(lclNum) || m_compiler->lvaGetDesc(lclNum)->IsHiddenBufferStructArg()); + assert(m_compiler->lvaVarAddrExposed(lclNum) || + ((m_lclAddrAssertions != nullptr) && m_lclAddrAssertions->IsMarkedForExposure(lclNum)) || + m_compiler->lvaGetDesc(lclNum)->IsHiddenBufferStructArg()); if (m_compiler->IsValidLclAddr(lclNum, offset)) { @@ -1408,6 +1795,59 @@ class LocalAddressVisitor final : public GenTreeVisitor } } + //----------------------------------------------------------------------------------- + // HandleLocalStoreAssertions: + // Handle clearing and generating assertions for a local store with the + // specified data value. + // + // Argument: + // store - The local store + // data - Value representing data + // + void HandleLocalStoreAssertions(GenTreeLclVarCommon* store, Value& data) + { + m_lclAddrAssertions->Clear(store->GetLclNum()); + + if (data.IsAddress() && store->OperIs(GT_STORE_LCL_VAR)) + { + LclVarDsc* dsc = m_compiler->lvaGetDesc(store); + // TODO-CQ: We currently don't handle promoted fields, but that has + // no impact since practically all promoted structs end up with + // lvHasLdAddrOp set. + if (!dsc->lvPromoted && !dsc->lvIsStructField && !dsc->lvHasLdAddrOp) + { + m_lclAddrAssertions->Record(store->GetLclNum(), data.LclNum(), data.Offset()); + } + } + } + + //----------------------------------------------------------------------------------- + // HandleLocalAssertions: + // Try to refine the specified "addr" value based on assertions about a specified local. + // + // Argument: + // lcl - The local node + // value - Value of local; will be modified to be an address if an assertion could be found + // + void HandleLocalAssertions(GenTreeLclVarCommon* lcl, Value& value) + { + assert(lcl->OperIs(GT_LCL_VAR)); + if (!lcl->TypeIs(TYP_I_IMPL, TYP_BYREF)) + { + return; + } + + const LocalEqualsLocalAddrAssertion* assertion = m_lclAddrAssertions->GetCurrentAssertion(lcl->GetLclNum()); + if (assertion != nullptr) + { + JITDUMP("Using assertion "); + DBEXEC(VERBOSE, assertion->Print()); + JITDUMP("\n"); + value.Address(assertion->AddressLclNum, assertion->AddressOffset); + m_propagatedAddrs = true; + } + } + public: //------------------------------------------------------------------------ // UpdateEarlyRefCount: updates the ref count for locals @@ -1499,41 +1939,43 @@ class LocalAddressVisitor final : public GenTreeVisitor // PhaseStatus Compiler::fgMarkAddressExposedLocals() { - bool madeChanges = false; - LocalSequencer sequencer(this); - LocalAddressVisitor visitor(this, opts.OptimizationEnabled() ? &sequencer : nullptr); + bool madeChanges = false; - for (BasicBlock* const block : Blocks()) + if (opts.OptimizationDisabled()) { - // Make the current basic block address available globally - compCurBB = block; - - for (Statement* const stmt : block->Statements()) + LocalAddressVisitor visitor(this, nullptr, nullptr); + for (BasicBlock* const block : Blocks()) { -#ifdef FEATURE_SIMD - if (opts.OptimizationEnabled() && stmt->GetRootNode()->TypeIs(TYP_FLOAT) && - stmt->GetRootNode()->OperIsStore()) - { - madeChanges |= fgMorphCombineSIMDFieldStores(block, stmt); - } -#endif + visitor.VisitBlock(block); + } + + madeChanges = visitor.MadeChanges(); + } + else + { + LocalEqualsLocalAddrAssertions assertions(this); + LocalEqualsLocalAddrAssertions* pAssertions = &assertions; - visitor.VisitStmt(stmt); +#ifdef DEBUG + static ConfigMethodRange s_range; + s_range.EnsureInit(JitConfig.JitEnableLocalAddrPropagationRange()); + if (!s_range.Contains(info.compMethodHash())) + { + pAssertions = nullptr; } +#endif - // We could check for GT_JMP inside the visitor, but this node is very - // rare so keeping it here avoids pessimizing the hot code. - if (block->endsWithJmpMethod(this)) + LocalSequencer sequencer(this); + LocalAddressVisitor visitor(this, &sequencer, pAssertions); + for (unsigned i = m_dfsTree->GetPostOrderCount(); i != 0; i--) { - // GT_JMP has implicit uses of all arguments. - for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++) - { - visitor.UpdateEarlyRefCount(lclNum, nullptr, nullptr); - } + visitor.VisitBlock(m_dfsTree->GetPostOrder(i - 1)); } - } - madeChanges |= visitor.MadeChanges(); + madeChanges = visitor.MadeChanges(); + + madeChanges |= fgExposeUnpropagatedLocals(visitor.PropagatedAnyAddresses(), &assertions); + } return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } @@ -1645,3 +2087,167 @@ bool Compiler::fgMorphCombineSIMDFieldStores(BasicBlock* block, Statement* stmt) return true; } #endif // FEATURE_SIMD + +//----------------------------------------------------------------------------------- +// fgExposeUnpropagatedLocals: +// Expose the final set of locals that were computed to have their address +// taken. Deletes LCL_ADDR nodes used as the data source of a store if the +// destination can be proven to not be read, and avoid exposing these if +// possible. +// +// Argument: +// propagatedAny - Whether any LCL_ADDR values were propagated +// assertions - Data structure tracking LCL_ADDR assertions +// +// Return Value: +// True if any changes were made to the IR; otherwise false. +// +bool Compiler::fgExposeUnpropagatedLocals(bool propagatedAny, LocalEqualsLocalAddrAssertions* assertions) +{ + if (!propagatedAny) + { + fgExposeLocalsInBitVec(assertions->GetLocalsToExpose()); + return false; + } + + BitVecTraits localsTraits(lvaCount, this); + BitVec unreadLocals = assertions->GetLocalsWithAssertions(); + + struct Store + { + Statement* Statement; + GenTreeLclVarCommon* Tree; + }; + + ArrayStack stores(getAllocator(CMK_LocalAddressVisitor)); + + for (unsigned i = m_dfsTree->GetPostOrderCount(); i != 0; i--) + { + BasicBlock* block = m_dfsTree->GetPostOrder(i - 1); + + for (Statement* stmt : block->Statements()) + { + for (GenTreeLclVarCommon* lcl : stmt->LocalsTreeList()) + { + if (!BitVecOps::IsMember(&localsTraits, unreadLocals, lcl->GetLclNum())) + { + continue; + } + + if (lcl->OperIs(GT_STORE_LCL_VAR, GT_STORE_LCL_FLD)) + { + if (lcl->TypeIs(TYP_I_IMPL, TYP_BYREF) && ((lcl->Data()->gtFlags & GTF_SIDE_EFFECT) == 0)) + { + stores.Push({stmt, lcl}); + } + } + else + { + BitVecOps::RemoveElemD(&localsTraits, unreadLocals, lcl->GetLclNum()); + } + } + } + } + + if (BitVecOps::IsEmpty(&localsTraits, unreadLocals)) + { + JITDUMP("No destinations of propagated LCL_ADDR nodes are unread\n"); + fgExposeLocalsInBitVec(assertions->GetLocalsToExpose()); + return false; + } + + bool changed = false; + for (int i = 0; i < stores.Height(); i++) + { + const Store& store = stores.BottomRef(i); + assert(store.Tree->TypeIs(TYP_I_IMPL, TYP_BYREF)); + + if (BitVecOps::IsMember(&localsTraits, unreadLocals, store.Tree->GetLclNum())) + { + JITDUMP("V%02u is unread; removing store data of [%06u]\n", store.Tree->GetLclNum(), dspTreeID(store.Tree)); + DISPTREE(store.Tree); + + store.Tree->Data()->BashToConst(0, store.Tree->Data()->TypeGet()); + fgSequenceLocals(store.Statement); + + JITDUMP("\nResult:\n"); + DISPTREE(store.Tree); + JITDUMP("\n"); + changed = true; + } + } + + if (changed) + { + // Finally compute new set of exposed locals. + BitVec exposedLocals(BitVecOps::MakeEmpty(&localsTraits)); + + for (unsigned i = m_dfsTree->GetPostOrderCount(); i != 0; i--) + { + BasicBlock* block = m_dfsTree->GetPostOrder(i - 1); + + for (Statement* stmt : block->Statements()) + { + for (GenTreeLclVarCommon* lcl : stmt->LocalsTreeList()) + { + if (!lcl->OperIs(GT_LCL_ADDR)) + { + continue; + } + + LclVarDsc* lclDsc = lvaGetDesc(lcl); + unsigned exposedLclNum = lclDsc->lvIsStructField ? lclDsc->lvParentLcl : lcl->GetLclNum(); + BitVecOps::AddElemD(&localsTraits, exposedLocals, exposedLclNum); + } + } + } + + auto dumpVars = [=, &localsTraits](BitVec_ValArg_T vec, BitVec_ValArg_T other) { + const char* sep = ""; + for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++) + { + if (BitVecOps::IsMember(&localsTraits, vec, lclNum)) + { + JITDUMP("%sV%02u", sep, lclNum); + sep = " "; + } + else if (BitVecOps::IsMember(&localsTraits, other, lclNum)) + { + JITDUMP("%s ", sep); + sep = " "; + } + } + }; + + // TODO-CQ: Instead of intersecting here, we should teach the above + // logic about retbuf LCL_ADDRs not leading to exposure. This should + // allow us to assert that exposedLocals is a subset of + // assertions->GetLocalsToExpose(). + BitVecOps::IntersectionD(&localsTraits, exposedLocals, assertions->GetLocalsToExpose()); + + JITDUMP("Old exposed set: "); + dumpVars(assertions->GetLocalsToExpose(), exposedLocals); + JITDUMP("\nNew exposed set: "); + dumpVars(exposedLocals, assertions->GetLocalsToExpose()); + JITDUMP("\n"); + + fgExposeLocalsInBitVec(exposedLocals); + } + + return changed; +} + +//----------------------------------------------------------------------------------- +// fgExposeLocalsInBitVec: +// Mark all locals in the bit vector as address exposed. +// +void Compiler::fgExposeLocalsInBitVec(BitVec_ValArg_T bitVec) +{ + BitVecTraits localsTraits(lvaCount, this); + BitVecOps::Iter iter(&localsTraits, bitVec); + unsigned lclNum; + while (iter.NextElem(&lclNum)) + { + lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS)); + } +} diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 2001f396c6f22..0a74288cb0b7e 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1974,7 +1974,8 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex BasicBlock* fastPreheader = fgNewBBafter(BBJ_ALWAYS, preheader, /*extendRegion*/ true); JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", fastPreheader->bbNum, preheader->bbNum); - fastPreheader->bbWeight = fastPreheader->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; + fastPreheader->bbWeight = preheader->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; + fastPreheader->CopyFlags(preheader, (BBF_PROF_WEIGHT | BBF_RUN_RARELY)); assert(preheader->KindIs(BBJ_ALWAYS)); assert(preheader->TargetIs(loop->GetHeader())); @@ -2008,6 +2009,7 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex BasicBlock* slowPreheader = fgNewBBafter(BBJ_ALWAYS, newPred, /*extendRegion*/ true); JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", slowPreheader->bbNum, newPred->bbNum); slowPreheader->bbWeight = newPred->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; + slowPreheader->CopyFlags(newPred, (BBF_PROF_WEIGHT | BBF_RUN_RARELY)); slowPreheader->scaleBBWeight(LoopCloneContext::slowPathWeightScaleFactor); newPred = slowPreheader; diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 91046c5474b73..cebda711b24f6 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -3382,6 +3382,31 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) } break; + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: + assert(hasImmediateOperand); + assert(varTypeIsIntegral(intrin.op2)); + assert(varTypeIsIntegral(intrin.op3)); + // Can only avoid generating a table if both immediates are constant. + if (intrin.op2->IsCnsIntOrI() && intrin.op3->IsCnsIntOrI()) + { + MakeSrcContained(node, intrin.op2); + MakeSrcContained(node, intrin.op3); + } + break; + default: unreached(); } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 22526033478ca..2599c7a8b4d84 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -3551,24 +3551,34 @@ GenTree* Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node) } } - if (simdSize == 32) + if (simdSize > 16) { - // We're creating a Vector256 scalar so we need to treat the original op as Vector128, - // we need to unsafely extend up to Vector256 (which is actually safe since the 128-bit - // op will zero extend up to 256-bits), and then we need to replace the original use - // with the new TYP_SIMD32 node. + assert((simdSize == 32) || (simdSize == 64)); + + // We're creating a Vector256/512 scalar so we need to treat the original op as Vector128, + // we need to unsafely extend up to Vector256/512 (which is actually safe since the 128-bit + // op will zero extend up to 256/512-bits), and then we need to replace the original use + // with the new TYP_SIMD32/64 node. node->ChangeType(TYP_SIMD16); node->SetSimdSize(16); LowerNode(node); - tmp2 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD32, node, NI_Vector128_ToVector256Unsafe, simdBaseJitType, - 16); - LIR::Use use; bool foundUse = BlockRange().TryGetUse(node, &use); + + tmp2 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD32, node, NI_Vector128_ToVector256Unsafe, simdBaseJitType, + 16); BlockRange().InsertAfter(node, tmp2); + if (simdSize == 64) + { + tmp3 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD64, tmp2, NI_Vector256_ToVector512Unsafe, + simdBaseJitType, 32); + BlockRange().InsertAfter(tmp2, tmp3); + tmp2 = tmp3; + } + if (foundUse) { use.ReplaceWith(tmp2); diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index b642d59bca05a..39c9251c74b6e 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -278,7 +278,7 @@ void LinearScan::updateNextFixedRef(RegRecord* regRecord, RefPosition* nextRefPo RefPosition* kill = nextKill; while ((kill != nullptr) && (kill->nodeLocation < nextLocation)) { - if ((kill->registerAssignment & genRegMask(regRecord->regNum)) != RBM_NONE) + if ((kill->registerAssignment & genSingleTypeRegMask(regRecord->regNum)) != RBM_NONE) { nextLocation = kill->nodeLocation; break; @@ -289,11 +289,11 @@ void LinearScan::updateNextFixedRef(RegRecord* regRecord, RefPosition* nextRefPo if (nextLocation == MaxLocation) { - fixedRegs &= ~genRegMask(regRecord->regNum); + fixedRegs.RemoveRegNumFromMask(regRecord->regNum); } else { - fixedRegs |= genRegMask(regRecord->regNum); + fixedRegs.AddRegNumInMask(regRecord->regNum); } nextFixedRef[regRecord->regNum] = nextLocation; @@ -304,12 +304,12 @@ SingleTypeRegSet LinearScan::getMatchingConstants(SingleTypeRegSet mask, RefPosition* refPosition) { assert(currentInterval->isConstant && RefTypeIsDef(refPosition->refType)); - SingleTypeRegSet candidates = (mask & m_RegistersWithConstants).GetRegSetForType(currentInterval->registerType); + SingleTypeRegSet candidates = mask & m_RegistersWithConstants.GetRegSetForType(currentInterval->registerType); SingleTypeRegSet result = RBM_NONE; while (candidates != RBM_NONE) { regNumber regNum = genFirstRegNumFromMask(candidates); - SingleTypeRegSet candidateBit = genRegMask(regNum); + SingleTypeRegSet candidateBit = genSingleTypeRegMask(regNum); candidates ^= candidateBit; RegRecord* physRegRecord = getRegisterRecord(regNum); @@ -398,29 +398,30 @@ void LinearScan::updateSpillCost(regNumber reg, Interval* interval) // assignedReg - Assigned register for this refposition. // void LinearScan::updateRegsFreeBusyState(RefPosition& refPosition, - regMaskTP regsBusy, + var_types registerType, + SingleTypeRegSet regsBusy, regMaskTP* regsToFree, regMaskTP* delayRegsToFree DEBUG_ARG(Interval* interval) DEBUG_ARG(regNumber assignedReg)) { - regsInUseThisLocation |= regsBusy; + regsInUseThisLocation.AddRegsetForType(regsBusy, registerType); if (refPosition.lastUse) { if (refPosition.delayRegFree) { INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE_DELAYED, interval, assignedReg)); - *delayRegsToFree |= regsBusy; - regsInUseNextLocation |= regsBusy; + delayRegsToFree->AddRegsetForType(regsBusy, registerType); + regsInUseNextLocation.AddRegsetForType(regsBusy, registerType); } else { INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE, interval, assignedReg)); - *regsToFree |= regsBusy; + regsToFree->AddRegsetForType(regsBusy, registerType); } } else if (refPosition.delayRegFree) { - regsInUseNextLocation |= regsBusy; + regsInUseNextLocation.AddRegsetForType(regsBusy, registerType); } } @@ -448,7 +449,11 @@ SingleTypeRegSet LinearScan::internalFloatRegCandidates() } else { +#ifdef TARGET_AMD64 + return RBM_FLT_CALLEE_TRASH.GetFloatRegSet(); +#else return RBM_FLT_CALLEE_TRASH; +#endif // TARGET_AMD64 } } @@ -597,7 +602,12 @@ SingleTypeRegSet LinearScan::stressLimitRegs(RefPosition* refPosition, RegisterT case LSRA_LIMIT_CALLER: { +#ifdef TARGET_XARCH + mask = getConstrainedRegMask(refPosition, regType, mask, RBM_CALLEE_TRASH.GetRegSetForType(regType), + minRegCount); +#else mask = getConstrainedRegMask(refPosition, regType, mask, RBM_CALLEE_TRASH, minRegCount); +#endif // TARGET_AMD64 } break; @@ -658,7 +668,7 @@ SingleTypeRegSet LinearScan::stressLimitRegs(RefPosition* refPosition, RegisterT bool LinearScan::conflictingFixedRegReference(regNumber regNum, RefPosition* refPosition) { // Is this a fixed reference of this register? If so, there is no conflict. - if (refPosition->isFixedRefOfRegMask(genRegMask(regNum))) + if (refPosition->isFixedRefOfRegMask(genSingleTypeRegMask(regNum))) { return false; } @@ -784,7 +794,7 @@ LinearScan::LinearScan(Compiler* theCompiler) #if defined(TARGET_XARCH) rbmAllMask = compiler->rbmAllMask; rbmMskCalleeTrash = compiler->rbmMskCalleeTrash; - memcpy(varTypeCalleeTrashRegs, compiler->varTypeCalleeTrashRegs, sizeof(SingleTypeRegSet) * TYP_COUNT); + memcpy(varTypeCalleeTrashRegs, compiler->varTypeCalleeTrashRegs, sizeof(regMaskTP) * TYP_COUNT); if (!compiler->canUseEvexEncoding()) { @@ -837,7 +847,7 @@ LinearScan::LinearScan(Compiler* theCompiler) // Note: one known reason why we exclude LR is because NativeAOT has dependency on not // using LR as a GPR. See: https://github.com/dotnet/runtime/issues/101932 // Once that is addressed, we may consider allowing LR in availableIntRegs. - availableIntRegs = ((RBM_ALLINT & ~(RBM_PR | RBM_FP | RBM_LR) & ~compiler->codeGen->regSet.rsMaskResvd)).getLow(); + availableIntRegs = ((RBM_ALLINT & ~(RBM_PR | RBM_FP | RBM_LR) & ~compiler->codeGen->regSet.rsMaskResvd.getLow())); #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) availableIntRegs = (RBM_ALLINT & ~(RBM_FP | RBM_RA) & ~compiler->codeGen->regSet.rsMaskResvd.getLow()); #else @@ -848,9 +858,17 @@ LinearScan::LinearScan(Compiler* theCompiler) availableIntRegs &= ~RBM_FPBASE; #endif // ETW_EBP_FRAMED +#ifdef TARGET_AMD64 + availableFloatRegs = RBM_ALLFLOAT.GetFloatRegSet(); + availableDoubleRegs = RBM_ALLDOUBLE.GetFloatRegSet(); +#else availableFloatRegs = RBM_ALLFLOAT; availableDoubleRegs = RBM_ALLDOUBLE; -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) +#endif + +#if defined(TARGET_XARCH) + availableMaskRegs = RBM_ALLMASK.GetPredicateRegSet(); +#elif defined(TARGET_ARM64) availableMaskRegs = RBM_ALLMASK; #endif @@ -2874,8 +2892,8 @@ bool copyOrMoveRegInUse(RefPosition* ref, LsraLocation loc) RegisterType LinearScan::getRegisterType(Interval* currentInterval, RefPosition* refPosition) { assert(refPosition->getInterval() == currentInterval); - RegisterType regType = currentInterval->registerType; - regMaskTP candidates = refPosition->registerAssignment; + RegisterType regType = currentInterval->registerType; + SingleTypeRegSet candidates = refPosition->registerAssignment; #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // The LoongArch64's ABI which the float args maybe passed by integer register // when no float register left but free integer register. @@ -3000,7 +3018,7 @@ regNumber LinearScan::allocateRegMinimal(Interval* currentInterva return REG_NA; } - foundReg = genRegNumFromMask(foundRegBit); + foundReg = genRegNumFromMask(foundRegBit, currentInterval->registerType); availablePhysRegRecord = getRegisterRecord(foundReg); Interval* assignedInterval = availablePhysRegRecord->assignedInterval; if ((assignedInterval != currentInterval) && @@ -3289,8 +3307,8 @@ bool LinearScan::isRefPositionActive(RefPosition* refPosition, LsraLocation refL // bool LinearScan::isSpillCandidate(Interval* current, RefPosition* refPosition, RegRecord* physRegRecord) { - regMaskTP candidateBit = genRegMask(physRegRecord->regNum); - LsraLocation refLocation = refPosition->nodeLocation; + SingleTypeRegSet candidateBit = genSingleTypeRegMask(physRegRecord->regNum); + LsraLocation refLocation = refPosition->nodeLocation; // We shouldn't be calling this if we haven't already determined that the register is not // busy until the next kill. assert(!isRegBusy(physRegRecord->regNum, current->registerType)); @@ -3507,8 +3525,7 @@ void LinearScan::checkAndAssignInterval(RegRecord* regRec, Interval* interval) // Assign the given physical register interval to the given interval void LinearScan::assignPhysReg(RegRecord* regRec, Interval* interval) { - SingleTypeRegSet assignedRegMask = genRegMask(regRec->regNum); - compiler->codeGen->regSet.rsSetRegsModified(assignedRegMask DEBUGARG(true)); + compiler->codeGen->regSet.rsSetRegsModified(genRegMask(regRec->regNum) DEBUGARG(true)); interval->assignedReg = regRec; checkAndAssignInterval(regRec, interval); @@ -3518,7 +3535,7 @@ void LinearScan::assignPhysReg(RegRecord* regRec, Interval* interval) if (interval->isLocalVar) { // Prefer this register for future references - interval->updateRegisterPreferences(assignedRegMask); + interval->updateRegisterPreferences(genSingleTypeRegMask(regRec->regNum)); } } @@ -4023,7 +4040,7 @@ void LinearScan::processKills(RefPosition* killRefPosition) RefPosition* nextKill = killRefPosition->nextRefPosition; regMaskTP killedRegs = killRefPosition->registerAssignment; - while (killedRegs != RBM_NONE) + while (killedRegs.IsNonEmpty()) { regNumber killedReg = genFirstRegNumFromMaskAndToggle(killedRegs); RegRecord* regRecord = getRegisterRecord(killedReg); @@ -4064,7 +4081,7 @@ void LinearScan::spillGCRefs(RefPosition* killRefPosition) { // For each physical register that can hold a GC type, // if it is occupied by an interval of a GC type, spill that interval. - regMaskTP candidateRegs = killRefPosition->registerAssignment; + SingleTypeRegSet candidateRegs = killRefPosition->registerAssignment; INDEBUG(bool killedRegs = false); while (candidateRegs != RBM_NONE) { @@ -4157,9 +4174,10 @@ regNumber LinearScan::rotateBlockStartLocation(Interval* interval, regNumber tar { // If we're rotating the register locations at block boundaries, try to use // the next higher register number of the appropriate register type. - regMaskTP candidateRegs = allRegs(interval->registerType) & availableRegs; - regNumber firstReg = REG_NA; - regNumber newReg = REG_NA; + SingleTypeRegSet candidateRegs = + allRegs(interval->registerType) & availableRegs.GetRegSetForType(interval->registerType); + regNumber firstReg = REG_NA; + regNumber newReg = REG_NA; while (candidateRegs != RBM_NONE) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(candidateRegs); @@ -4558,7 +4576,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) assert(targetReg != REG_STK); assert(interval->assignedReg != nullptr && interval->assignedReg->regNum == targetReg && interval->assignedReg->assignedInterval == interval); - liveRegs |= getRegMask(targetReg, interval->registerType); + liveRegs.AddRegNum(targetReg, interval->registerType); continue; } } @@ -4588,7 +4606,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) // likely to match other assignments this way. targetReg = interval->physReg; interval->isActive = true; - liveRegs |= getRegMask(targetReg, interval->registerType); + liveRegs.AddRegNum(targetReg, interval->registerType); INDEBUG(inactiveRegs |= genRegMask(targetReg)); setVarReg(inVarToRegMap, varIndex, targetReg); } @@ -4600,7 +4618,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) if (targetReg != REG_STK) { RegRecord* targetRegRecord = getRegisterRecord(targetReg); - liveRegs |= getRegMask(targetReg, interval->registerType); + liveRegs.AddRegNum(targetReg, interval->registerType); if (!allocationPassComplete) { updateNextIntervalRef(targetReg, interval); @@ -4637,7 +4655,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) RegRecord* anotherHalfRegRec = findAnotherHalfRegRec(targetRegRecord); // Use TYP_FLOAT to get the regmask of just the half reg. - liveRegs &= ~getRegMask(anotherHalfRegRec->regNum, TYP_FLOAT); + liveRegs.RemoveRegNum(anotherHalfRegRec->regNum, TYP_FLOAT); } #endif // TARGET_ARM @@ -4645,7 +4663,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) assignPhysReg(targetRegRecord, interval); } if (interval->recentRefPosition != nullptr && !interval->recentRefPosition->copyReg && - interval->recentRefPosition->registerAssignment != genRegMask(targetReg)) + interval->recentRefPosition->registerAssignment != genSingleTypeRegMask(targetReg)) { interval->getNextRefPosition()->outOfOrder = true; } @@ -4664,7 +4682,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) for (regNumber reg = REG_FIRST; reg < AVAILABLE_REG_COUNT; reg = REG_NEXT(reg)) { RegRecord* physRegRecord = getRegisterRecord(reg); - if ((liveRegs & genRegMask(reg)) == 0) + if (!liveRegs.IsRegNumInMask(reg)) { makeRegAvailable(reg, physRegRecord->registerType); Interval* assignedInterval = physRegRecord->assignedInterval; @@ -4726,7 +4744,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) // Only focus on actual registers present deadCandidates &= actualRegistersMask; - while (deadCandidates != RBM_NONE) + while (deadCandidates.IsNonEmpty()) { regNumber reg = genFirstRegNumFromMaskAndToggle(deadCandidates); RegRecord* physRegRecord = getRegisterRecord(reg); @@ -4915,14 +4933,14 @@ void LinearScan::freeRegister(RegRecord* physRegRecord) // void LinearScan::freeRegisters(regMaskTP regsToFree) { - if (regsToFree == RBM_NONE) + if (regsToFree.IsEmpty()) { return; } INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_FREE_REGS)); makeRegsAvailable(regsToFree); - while (regsToFree != RBM_NONE) + while (regsToFree.IsNonEmpty()) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(regsToFree); @@ -4931,7 +4949,7 @@ void LinearScan::freeRegisters(regMaskTP regsToFree) if (regRecord->assignedInterval != nullptr && (regRecord->assignedInterval->registerType == TYP_DOUBLE)) { assert(genIsValidDoubleReg(nextReg)); - regsToFree ^= genRegMask(regNumber(nextReg + 1)); + regsToFree.RemoveRegNumFromMask(regNumber(nextReg + 1)); } #endif freeRegister(regRecord); @@ -5010,7 +5028,7 @@ void LinearScan::allocateRegistersMinimal() // mess with the dump, since this was previously being done before the call below // to dumpRegRecords. regMaskTP tempRegsToMakeInactive = (regsToMakeInactive | delayRegsToMakeInactive); - while (tempRegsToMakeInactive != RBM_NONE) + while (tempRegsToMakeInactive.IsNonEmpty()) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(tempRegsToMakeInactive); RegRecord* regRecord = getRegisterRecord(nextReg); @@ -5084,10 +5102,10 @@ void LinearScan::allocateRegistersMinimal() copyRegsToFree = RBM_NONE; regsInUseThisLocation = regsInUseNextLocation; regsInUseNextLocation = RBM_NONE; - if ((regsToFree | delayRegsToFree) != RBM_NONE) + if ((regsToFree | delayRegsToFree).IsNonEmpty()) { freeRegisters(regsToFree); - if ((currentLocation > (prevLocation + 1)) && (delayRegsToFree != RBM_NONE)) + if ((currentLocation > (prevLocation + 1)) && (delayRegsToFree.IsNonEmpty())) { // We should never see a delayReg that is delayed until a Location that has no RefPosition // (that would be the RefPosition that it was supposed to interfere with). @@ -5203,13 +5221,13 @@ void LinearScan::allocateRegistersMinimal() } #endif // TARGET_ARM } - regsInUseThisLocation |= currentRefPosition.registerAssignment; + regsInUseThisLocation.AddRegsetForType(currentRefPosition.registerAssignment, regRecord->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_FIXED_REG, nullptr, currentRefPosition.assignedReg())); #ifdef SWIFT_SUPPORT if (currentRefPosition.delayRegFree) { - regsInUseNextLocation |= currentRefPosition.registerAssignment; + regsInUseNextLocation.AddRegsetForType(currentRefPosition.registerAssignment, regRecord->registerType); } #endif // SWIFT_SUPPORT continue; @@ -5265,7 +5283,7 @@ void LinearScan::allocateRegistersMinimal() if (assignedRegister != REG_NA) { isInRegister = true; - assignedRegBit = genRegMask(assignedRegister); + assignedRegBit = genSingleTypeRegMask(assignedRegister); if (!currentInterval->isActive) { assert(!RefTypeIsUse(refType)); @@ -5309,7 +5327,7 @@ void LinearScan::allocateRegistersMinimal() setIntervalAsSplit(currentInterval); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_MOVE_REG, currentInterval, assignedRegister)); } - else if ((genRegMask(assignedRegister) & currentRefPosition.registerAssignment) != 0) + else if ((genSingleTypeRegMask(assignedRegister) & currentRefPosition.registerAssignment) != 0) { currentRefPosition.registerAssignment = assignedRegBit; if (!currentInterval->isActive) @@ -5325,20 +5343,22 @@ void LinearScan::allocateRegistersMinimal() { regNumber copyReg = assignCopyRegMinimal(¤tRefPosition); - lastAllocatedRefPosition = ¤tRefPosition; - regMaskTP copyRegMask = getRegMask(copyReg, currentInterval->registerType); - regMaskTP assignedRegMask = getRegMask(assignedRegister, currentInterval->registerType); + lastAllocatedRefPosition = ¤tRefPosition; + SingleTypeRegSet copyRegMask = getSingleTypeRegMask(copyReg, currentInterval->registerType); + SingleTypeRegSet assignedRegMask = + getSingleTypeRegMask(assignedRegister, currentInterval->registerType); // For consecutive register, although it shouldn't matter what the assigned register was, // because we have just assigned it `copyReg` and that's the one in-use, and not the // one that was assigned previously. However, in situation where an upper-vector restore // happened to be restored in assignedReg, we would need assignedReg to stay alive because // we will copy the entire vector value from it to the `copyReg`. - updateRegsFreeBusyState(currentRefPosition, assignedRegMask | copyRegMask, ®sToFree, + updateRegsFreeBusyState(currentRefPosition, currentInterval->registerType, + assignedRegMask | copyRegMask, ®sToFree, &delayRegsToFree DEBUG_ARG(currentInterval) DEBUG_ARG(assignedRegister)); if (!currentRefPosition.lastUse) { - copyRegsToFree |= copyRegMask; + copyRegsToFree.AddRegsetForType(copyRegMask, currentInterval->registerType); } // For tree temp (non-localVar) interval, we will need an explicit move. @@ -5353,7 +5373,7 @@ void LinearScan::allocateRegistersMinimal() else { INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_NEEDS_NEW_REG, nullptr, assignedRegister)); - regsToFree |= getRegMask(assignedRegister, currentInterval->registerType); + regsToFree.AddRegNum(assignedRegister, currentInterval->registerType); // We want a new register, but we don't want this to be considered a spill. assignedRegister = REG_NA; if (physRegRecord->assignedInterval == currentInterval) @@ -5402,7 +5422,8 @@ void LinearScan::allocateRegistersMinimal() if (currentRefPosition.isFixedRegRef && !currentInterval->isActive && (currentInterval->assignedReg != nullptr) && (currentInterval->assignedReg->assignedInterval == currentInterval) && - (genRegMask(currentInterval->assignedReg->regNum) != currentRefPosition.registerAssignment)) + (genSingleTypeRegMask(currentInterval->assignedReg->regNum) != + currentRefPosition.registerAssignment)) { unassignPhysReg(currentInterval->assignedReg, nullptr); } @@ -5450,17 +5471,18 @@ void LinearScan::allocateRegistersMinimal() // If we allocated a register, record it if (assignedRegister != REG_NA) { - assignedRegBit = genRegMask(assignedRegister); - regMaskTP regMask = getRegMask(assignedRegister, currentInterval->registerType); - regsInUseThisLocation |= regMask; + assignedRegBit = genSingleTypeRegMask(assignedRegister); + SingleTypeRegSet regMask = getSingleTypeRegMask(assignedRegister, currentInterval->registerType); + regsInUseThisLocation.AddRegsetForType(regMask, currentInterval->registerType); if (currentRefPosition.delayRegFree) { - regsInUseNextLocation |= regMask; + regsInUseNextLocation.AddRegsetForType(regMask, currentInterval->registerType); } currentRefPosition.registerAssignment = assignedRegBit; currentInterval->physReg = assignedRegister; - regsToFree &= ~regMask; // we'll set it again later if it's dead + regsToFree.RemoveRegsetForType(regMask, currentInterval->registerType); // we'll set it again later if it's + // dead // If this interval is dead, free the register. // The interval could be dead if this is a user variable, or if the @@ -5481,11 +5503,11 @@ void LinearScan::allocateRegistersMinimal() { if (currentRefPosition.delayRegFree) { - delayRegsToMakeInactive |= regMask; + delayRegsToMakeInactive.AddRegsetForType(regMask, currentInterval->registerType); } else { - regsToMakeInactive |= regMask; + regsToMakeInactive.AddRegsetForType(regMask, currentInterval->registerType); } // TODO-Cleanup: this makes things consistent with previous, and will enable preferences // to be propagated, but it seems less than ideal. @@ -5504,13 +5526,13 @@ void LinearScan::allocateRegistersMinimal() { if (currentRefPosition.delayRegFree) { - delayRegsToFree |= regMask; + delayRegsToFree.AddRegsetForType(regMask, currentInterval->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE_DELAYED)); } else { - regsToFree |= regMask; + regsToFree.AddRegsetForType(regMask, currentInterval->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE)); } @@ -5638,7 +5660,7 @@ void LinearScan::allocateRegisters() updateNextIntervalRef(reg, interval); updateSpillCost(reg, interval); setRegInUse(reg, interval->registerType); - INDEBUG(registersToDump |= getRegMask(reg, interval->registerType)); + INDEBUG(registersToDump.AddRegNum(reg, interval->registerType)); } } else @@ -5692,7 +5714,7 @@ void LinearScan::allocateRegisters() // mess with the dump, since this was previously being done before the call below // to dumpRegRecords. regMaskTP tempRegsToMakeInactive = (regsToMakeInactive | delayRegsToMakeInactive); - while (tempRegsToMakeInactive != RBM_NONE) + while (tempRegsToMakeInactive.IsNonEmpty()) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(tempRegsToMakeInactive); RegRecord* regRecord = getRegisterRecord(nextReg); @@ -5770,10 +5792,10 @@ void LinearScan::allocateRegisters() consecutiveRegsInUseThisLocation = RBM_NONE; } #endif - if ((regsToFree | delayRegsToFree) != RBM_NONE) + if ((regsToFree | delayRegsToFree).IsNonEmpty()) { freeRegisters(regsToFree); - if ((currentLocation > (prevLocation + 1)) && (delayRegsToFree != RBM_NONE)) + if ((currentLocation > (prevLocation + 1)) && (delayRegsToFree.IsNonEmpty())) { // We should never see a delayReg that is delayed until a Location that has no RefPosition // (that would be the RefPosition that it was supposed to interfere with). @@ -5913,13 +5935,13 @@ void LinearScan::allocateRegisters() } #endif // TARGET_ARM } - regsInUseThisLocation |= currentRefPosition.registerAssignment; + regsInUseThisLocation.AddRegsetForType(currentRefPosition.registerAssignment, regRecord->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_FIXED_REG, nullptr, currentRefPosition.assignedReg())); #ifdef SWIFT_SUPPORT if (currentRefPosition.delayRegFree) { - regsInUseNextLocation |= currentRefPosition.registerAssignment; + regsInUseNextLocation.AddRegsetForType(currentRefPosition.registerAssignment, regRecord->registerType); } #endif // SWIFT_SUPPORT continue; @@ -6056,7 +6078,7 @@ void LinearScan::allocateRegisters() updateSpillCost(assignedRegister, currentInterval); } - regsToFree |= getRegMask(assignedRegister, currentInterval->registerType); + regsToFree.AddRegNum(assignedRegister, currentInterval->registerType); } INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_NO_REG_ALLOCATED, nullptr, assignedRegister)); currentRefPosition.registerAssignment = RBM_NONE; @@ -6153,7 +6175,7 @@ void LinearScan::allocateRegisters() // kill would lead to spill of source but not the putarg_reg if it were treated // as special. if (srcInterval->isActive && - genRegMask(srcInterval->physReg) == currentRefPosition.registerAssignment && + genSingleTypeRegMask(srcInterval->physReg) == currentRefPosition.registerAssignment && currentInterval->getNextRefLocation() == nextFixedRef[srcInterval->physReg]) { assert(physRegRecord->regNum == srcInterval->physReg); @@ -6197,7 +6219,7 @@ void LinearScan::allocateRegisters() if (assignedRegister != REG_NA) { isInRegister = true; - assignedRegBit = genRegMask(assignedRegister); + assignedRegBit = genSingleTypeRegMask(assignedRegister); if (!currentInterval->isActive) { // If this is a use, it must have started the block on the stack, but the register @@ -6239,9 +6261,9 @@ void LinearScan::allocateRegisters() // it might be beneficial to keep it in this reg for PART of the lifetime if (currentInterval->isLocalVar) { - regMaskTP preferences = currentInterval->registerPreferences; - bool keepAssignment = true; - bool matchesPreferences = (preferences & genRegMask(assignedRegister)) != RBM_NONE; + SingleTypeRegSet preferences = currentInterval->registerPreferences; + bool keepAssignment = true; + bool matchesPreferences = (preferences & genSingleTypeRegMask(assignedRegister)) != RBM_NONE; // Will the assigned register cover the lifetime? If not, does it at least // meet the preferences for the next RefPosition? @@ -6322,7 +6344,7 @@ void LinearScan::allocateRegisters() setIntervalAsSplit(currentInterval); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_MOVE_REG, currentInterval, assignedRegister)); } - else if ((genRegMask(assignedRegister) & currentRefPosition.registerAssignment) != 0) + else if ((genSingleTypeRegMask(assignedRegister) & currentRefPosition.registerAssignment) != 0) { #ifdef TARGET_ARM64 if (hasConsecutiveRegister && currentRefPosition.isFirstRefPositionOfConsecutiveRegisters()) @@ -6347,9 +6369,10 @@ void LinearScan::allocateRegisters() if (copyReg != assignedRegister) { - lastAllocatedRefPosition = ¤tRefPosition; - regMaskTP copyRegMask = getRegMask(copyReg, currentInterval->registerType); - regMaskTP assignedRegMask = getRegMask(assignedRegister, currentInterval->registerType); + lastAllocatedRefPosition = ¤tRefPosition; + SingleTypeRegSet copyRegMask = getSingleTypeRegMask(copyReg, currentInterval->registerType); + SingleTypeRegSet assignedRegMask = + getSingleTypeRegMask(assignedRegister, currentInterval->registerType); if ((consecutiveRegsInUseThisLocation & assignedRegMask) != RBM_NONE) { @@ -6367,12 +6390,13 @@ void LinearScan::allocateRegisters() // happened to be restored in assignedReg, we would need assignedReg to stay alive because // we will copy the entire vector value from it to the `copyReg`. - updateRegsFreeBusyState(currentRefPosition, assignedRegMask | copyRegMask, ®sToFree, + updateRegsFreeBusyState(currentRefPosition, currentInterval->registerType, + assignedRegMask | copyRegMask, ®sToFree, &delayRegsToFree DEBUG_ARG(currentInterval) DEBUG_ARG(assignedRegister)); if (!currentRefPosition.lastUse) { - copyRegsToFree |= copyRegMask; + copyRegsToFree.AddRegsetForType(copyRegMask, currentInterval->registerType); } // If this is a tree temp (non-localVar) interval, we will need an explicit move. @@ -6445,9 +6469,10 @@ void LinearScan::allocateRegisters() copyReg = assignCopyReg(¤tRefPosition); } - lastAllocatedRefPosition = ¤tRefPosition; - regMaskTP copyRegMask = getRegMask(copyReg, currentInterval->registerType); - regMaskTP assignedRegMask = getRegMask(assignedRegister, currentInterval->registerType); + lastAllocatedRefPosition = ¤tRefPosition; + SingleTypeRegSet copyRegMask = getSingleTypeRegMask(copyReg, currentInterval->registerType); + SingleTypeRegSet assignedRegMask = + getSingleTypeRegMask(assignedRegister, currentInterval->registerType); #ifdef TARGET_ARM64 if (hasConsecutiveRegister && currentRefPosition.needsConsecutive) @@ -6476,11 +6501,12 @@ void LinearScan::allocateRegisters() // one that was assigned previously. However, in situation where an upper-vector restore // happened to be restored in assignedReg, we would need assignedReg to stay alive because // we will copy the entire vector value from it to the `copyReg`. - updateRegsFreeBusyState(currentRefPosition, assignedRegMask | copyRegMask, ®sToFree, + updateRegsFreeBusyState(currentRefPosition, currentInterval->registerType, + assignedRegMask | copyRegMask, ®sToFree, &delayRegsToFree DEBUG_ARG(currentInterval) DEBUG_ARG(assignedRegister)); if (!currentRefPosition.lastUse) { - copyRegsToFree |= copyRegMask; + copyRegsToFree.AddRegsetForType(copyRegMask, currentInterval->registerType); } // If this is a tree temp (non-localVar) interval, we will need an explicit move. @@ -6501,7 +6527,7 @@ void LinearScan::allocateRegisters() else { INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_NEEDS_NEW_REG, nullptr, assignedRegister)); - regsToFree |= getRegMask(assignedRegister, currentInterval->registerType); + regsToFree.AddRegNum(assignedRegister, currentInterval->registerType); // We want a new register, but we don't want this to be considered a spill. assignedRegister = REG_NA; if (physRegRecord->assignedInterval == currentInterval) @@ -6600,7 +6626,8 @@ void LinearScan::allocateRegisters() if (currentRefPosition.isFixedRegRef && !currentInterval->isActive && (currentInterval->assignedReg != nullptr) && (currentInterval->assignedReg->assignedInterval == currentInterval) && - (genRegMask(currentInterval->assignedReg->regNum) != currentRefPosition.registerAssignment)) + (genSingleTypeRegMask(currentInterval->assignedReg->regNum) != + currentRefPosition.registerAssignment)) { unassignPhysReg(currentInterval->assignedReg, nullptr); } @@ -6668,17 +6695,18 @@ void LinearScan::allocateRegisters() // If we allocated a register, record it if (assignedRegister != REG_NA) { - assignedRegBit = genRegMask(assignedRegister); - regMaskTP regMask = getRegMask(assignedRegister, currentInterval->registerType); - regsInUseThisLocation |= regMask; + assignedRegBit = genSingleTypeRegMask(assignedRegister); + SingleTypeRegSet regMask = getSingleTypeRegMask(assignedRegister, currentInterval->registerType); + regsInUseThisLocation.AddRegsetForType(regMask, currentInterval->registerType); if (currentRefPosition.delayRegFree) { - regsInUseNextLocation |= regMask; + regsInUseNextLocation.AddRegsetForType(regMask, currentInterval->registerType); } currentRefPosition.registerAssignment = assignedRegBit; currentInterval->physReg = assignedRegister; - regsToFree &= ~regMask; // we'll set it again later if it's dead + regsToFree.RemoveRegsetForType(regMask, currentInterval->registerType); // we'll set it again later if it's + // dead // If this interval is dead, free the register. // The interval could be dead if this is a user variable, or if the @@ -6715,11 +6743,11 @@ void LinearScan::allocateRegisters() { if (currentRefPosition.delayRegFree) { - delayRegsToMakeInactive |= regMask; + delayRegsToMakeInactive.AddRegsetForType(regMask, currentInterval->registerType); } else { - regsToMakeInactive |= regMask; + regsToMakeInactive.AddRegsetForType(regMask, currentInterval->registerType); } // TODO-Cleanup: this makes things consistent with previous, and will enable preferences // to be propagated, but it seems less than ideal. @@ -6738,13 +6766,13 @@ void LinearScan::allocateRegisters() { if (currentRefPosition.delayRegFree) { - delayRegsToFree |= regMask; + delayRegsToFree.AddRegsetForType(regMask, currentInterval->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE_DELAYED)); } else { - regsToFree |= regMask; + regsToFree.AddRegsetForType(regMask, currentInterval->registerType); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_LAST_USE)); } @@ -7576,8 +7604,15 @@ void LinearScan::insertUpperVectorSave(GenTree* tree, saveLcl->SetRegNum(lclVarReg); SetLsraAdded(saveLcl); - GenTreeIntrinsic* simdUpperSave = - new (compiler, GT_INTRINSIC) GenTreeIntrinsic(LargeVectorSaveType, saveLcl, NI_SIMD_UpperSave, nullptr); +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP entryPoint; + + entryPoint.addr = nullptr; + entryPoint.accessType = IAT_VALUE; +#endif // FEATURE_READYTORUN + + GenTreeIntrinsic* simdUpperSave = new (compiler, GT_INTRINSIC) + GenTreeIntrinsic(LargeVectorSaveType, saveLcl, NI_SIMD_UpperSave, nullptr R2RARG(entryPoint)); SetLsraAdded(simdUpperSave); simdUpperSave->SetRegNum(spillReg); @@ -7634,8 +7669,15 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree, restoreLcl->SetRegNum(lclVarReg); SetLsraAdded(restoreLcl); - GenTreeIntrinsic* simdUpperRestore = - new (compiler, GT_INTRINSIC) GenTreeIntrinsic(varDsc->TypeGet(), restoreLcl, NI_SIMD_UpperRestore, nullptr); +#if defined(FEATURE_READYTORUN) + CORINFO_CONST_LOOKUP entryPoint; + + entryPoint.addr = nullptr; + entryPoint.accessType = IAT_VALUE; +#endif // FEATURE_READYTORUN + + GenTreeIntrinsic* simdUpperRestore = new (compiler, GT_INTRINSIC) + GenTreeIntrinsic(varDsc->TypeGet(), restoreLcl, NI_SIMD_UpperRestore, nullptr R2RARG(entryPoint)); regNumber restoreReg = upperVectorInterval->physReg; SetLsraAdded(simdUpperRestore); @@ -8106,7 +8148,7 @@ void LinearScan::resolveRegisters() { // If the localVar is in a register, it must be in a register that is not trashed by // the current node (otherwise it would have already been spilled). - assert((genRegMask(localVarInterval->physReg) & getKillSetForNode(treeNode)) == RBM_NONE); + assert((genRegMask(localVarInterval->physReg) & getKillSetForNode(treeNode)).IsEmpty()); // If we have allocated a register to spill it to, we will use that; otherwise, we will // spill it to the stack. We can use as a temp register any non-arg caller-save register. currentRefPosition->referent->recentRefPosition = currentRefPosition; @@ -8362,10 +8404,10 @@ void LinearScan::resolveRegisters() if (varDsc->lvIsParam) { - regMaskTP initialRegMask = interval->firstRefPosition->registerAssignment; - regNumber initialReg = (initialRegMask == RBM_NONE || interval->firstRefPosition->spillAfter) - ? REG_STK - : genRegNumFromMask(initialRegMask); + SingleTypeRegSet initialRegMask = interval->firstRefPosition->registerAssignment; + regNumber initialReg = (initialRegMask == RBM_NONE || interval->firstRefPosition->spillAfter) + ? REG_STK + : genRegNumFromMask(initialRegMask, interval->registerType); #ifdef TARGET_ARM if (varTypeIsMultiReg(varDsc)) @@ -8451,7 +8493,7 @@ void LinearScan::resolveRegisters() varDsc->lvOnFrame = false; } #ifdef DEBUG - regMaskTP registerAssignment = genRegMask(varDsc->GetRegNum()); + SingleTypeRegSet registerAssignment = genSingleTypeRegMask(varDsc->GetRegNum()); assert(!interval->isSpilled && !interval->isSplit); RefPosition* refPosition = interval->firstRefPosition; assert(refPosition != nullptr); @@ -8731,7 +8773,7 @@ regNumber LinearScan::getTempRegForResolution(BasicBlock* fromBlock, assert(fromReg != REG_NA); if (fromReg != REG_STK) { - freeRegs &= ~genRegMask(fromReg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); + freeRegs &= ~genSingleTypeRegMask(fromReg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); } if (toBlock != nullptr) @@ -8740,7 +8782,7 @@ regNumber LinearScan::getTempRegForResolution(BasicBlock* fromBlock, assert(toReg != REG_NA); if (toReg != REG_STK) { - freeRegs &= ~genRegMask(toReg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); + freeRegs &= ~genSingleTypeRegMask(toReg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); } } } @@ -8759,7 +8801,7 @@ regNumber LinearScan::getTempRegForResolution(BasicBlock* fromBlock, assert(reg != REG_NA); if (reg != REG_STK) { - freeRegs &= ~genRegMask(reg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); + freeRegs &= ~genSingleTypeRegMask(reg ARM_ARG(getIntervalForLocalVar(varIndex)->registerType)); } } } @@ -8782,10 +8824,14 @@ regNumber LinearScan::getTempRegForResolution(BasicBlock* fromBlock, // Prefer a callee-trashed register if possible to prevent new prolog/epilog saves/restores. if ((freeRegs & RBM_CALLEE_TRASH) != 0) { +#ifdef TARGET_XARCH + freeRegs &= RBM_CALLEE_TRASH.GetRegSetForType(type); +#else freeRegs &= RBM_CALLEE_TRASH; +#endif } - regNumber tempReg = genRegNumFromMask(genFindLowestBit(freeRegs)); + regNumber tempReg = genRegNumFromMask(genFindLowestBit(freeRegs), type); return tempReg; } } @@ -8984,8 +9030,7 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) regNumber fromReg = getVarReg(outVarToRegMap, liveOutVarIndex); if (fromReg != REG_STK) { - regMaskTP fromRegMask = genRegMask(fromReg, getIntervalForLocalVar(liveOutVarIndex)->registerType); - liveOutRegs |= fromRegMask; + liveOutRegs.AddRegNumInMask(fromReg ARM_ARG(getIntervalForLocalVar(liveOutVarIndex)->registerType)); } } @@ -9010,17 +9055,17 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) noway_assert(op1 != nullptr && op2 != nullptr); assert(op1->GetRegNum() != REG_NA && op2->GetRegNum() != REG_NA); // No floating point values, so no need to worry about the register type - // (i.e. for ARM32, where we used the genRegMask overload with a type). + // (i.e. for ARM32, where we used the genSingleTypeRegMask overload with a type). assert(varTypeIsIntegralOrI(op1) && varTypeIsIntegralOrI(op2)); - consumedRegs |= genRegMask(op1->GetRegNum()); - consumedRegs |= genRegMask(op2->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(op1->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(op2->GetRegNum()); // Special handling for GT_COPY to not resolve into the source // of switch's operand. if (op1->OperIs(GT_COPY)) { GenTree* srcOp1 = op1->gtGetOp1(); - consumedRegs |= genRegMask(srcOp1->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(srcOp1->GetRegNum()); } else if (op1->IsLocal()) { @@ -9030,7 +9075,7 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) if (op2->OperIs(GT_COPY)) { GenTree* srcOp2 = op2->gtGetOp1(); - consumedRegs |= genRegMask(srcOp2->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(srcOp2->GetRegNum()); } else if (op2->IsLocal()) { @@ -9058,12 +9103,12 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) if (lastNode->OperIs(GT_JTRUE, GT_JCMP, GT_JTEST)) { GenTree* op = lastNode->gtGetOp1(); - consumedRegs |= genRegMask(op->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(op->GetRegNum()); if (op->OperIs(GT_COPY)) { GenTree* srcOp = op->gtGetOp1(); - consumedRegs |= genRegMask(srcOp->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(srcOp->GetRegNum()); } else if (op->IsLocal()) { @@ -9074,12 +9119,12 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) if (lastNode->OperIs(GT_JCMP, GT_JTEST) && !lastNode->gtGetOp2()->isContained()) { op = lastNode->gtGetOp2(); - consumedRegs |= genRegMask(op->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(op->GetRegNum()); if (op->OperIs(GT_COPY)) { GenTree* srcOp = op->gtGetOp1(); - consumedRegs |= genRegMask(srcOp->GetRegNum()); + consumedRegs |= genSingleTypeRegMask(srcOp->GetRegNum()); } else if (op->IsLocal()) { @@ -9144,14 +9189,16 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) // We only need to check for these cases if sameToReg is an actual register (not REG_STK). if (sameToReg != REG_NA && sameToReg != REG_STK) { + var_types outVarRegType = getIntervalForLocalVar(outResolutionSetVarIndex)->registerType; + // If there's a path on which this var isn't live, it may use the original value in sameToReg. // In this case, sameToReg will be in the liveOutRegs of this block. // Similarly, if sameToReg is in sameWriteRegs, it has already been used (i.e. for a lclVar that's // live only at another target), and we can't copy another lclVar into that reg in this block. - regMaskTP sameToRegMask = - genRegMask(sameToReg, getIntervalForLocalVar(outResolutionSetVarIndex)->registerType); - if (maybeSameLivePaths && - (((sameToRegMask & liveOutRegs) != RBM_NONE) || ((sameToRegMask & sameWriteRegs) != RBM_NONE))) + SingleTypeRegSet sameToRegMask = + genSingleTypeRegMask(sameToReg, getIntervalForLocalVar(outResolutionSetVarIndex)->registerType); + if (maybeSameLivePaths && (liveOutRegs.IsRegNumInMask(sameToReg ARM_ARG(outVarRegType)) || + sameWriteRegs.IsRegNumInMask(sameToReg ARM_ARG(outVarRegType)))) { sameToReg = REG_NA; } @@ -9193,7 +9240,8 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) VarSetOps::AddElemD(compiler, diffResolutionSet, outResolutionSetVarIndex); if (fromReg != REG_STK) { - diffReadRegs |= genRegMask(fromReg, getIntervalForLocalVar(outResolutionSetVarIndex)->registerType); + diffReadRegs.AddRegNumInMask( + fromReg ARM_ARG(getIntervalForLocalVar(outResolutionSetVarIndex)->registerType)); } } else if (sameToReg != fromReg) @@ -9202,14 +9250,15 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) setVarReg(sameVarToRegMap, outResolutionSetVarIndex, sameToReg); if (sameToReg != REG_STK) { - sameWriteRegs |= genRegMask(sameToReg, getIntervalForLocalVar(outResolutionSetVarIndex)->registerType); + sameWriteRegs.AddRegNumInMask( + sameToReg ARM_ARG(getIntervalForLocalVar(outResolutionSetVarIndex)->registerType)); } } } if (!VarSetOps::IsEmpty(compiler, sameResolutionSet)) { - if ((sameWriteRegs & diffReadRegs) != RBM_NONE) + if ((sameWriteRegs & diffReadRegs).IsNonEmpty()) { // We cannot split the "same" and "diff" regs if the "same" set writes registers // that must be read by the "diff" set. (Note that when these are done as a "batch" @@ -9723,11 +9772,10 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, // First, find all the ones that are ready to move now regMaskTP targetCandidates = targetRegsToDo; - while (targetCandidates != RBM_NONE) + while (targetCandidates.IsNonEmpty()) { - regNumber targetReg = genFirstRegNumFromMask(targetCandidates); - regMaskTP targetRegMask = genRegMask(targetReg); - targetCandidates ^= targetRegMask; + regNumber targetReg = genFirstRegNumFromMask(targetCandidates); + targetCandidates.RemoveRegNumFromMask(targetReg); if (location[targetReg] == REG_NA) { #ifdef TARGET_ARM @@ -9740,26 +9788,25 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, regNumber anotherHalfRegNum = REG_NEXT(targetReg); if (location[anotherHalfRegNum] == REG_NA) { - targetRegsReady |= targetRegMask; + targetRegsReady.AddRegNumInMask(targetReg); } } else #endif // TARGET_ARM { - targetRegsReady |= targetRegMask; + targetRegsReady.AddRegNumInMask(targetReg); } } } // Perform reg to reg moves - while (targetRegsToDo != RBM_NONE) + while (targetRegsToDo.IsNonEmpty()) { - while (targetRegsReady != RBM_NONE) + while (targetRegsReady.IsNonEmpty()) { - regNumber targetReg = genFirstRegNumFromMask(targetRegsReady); - regMaskTP targetRegMask = genRegMask(targetReg); - targetRegsToDo ^= targetRegMask; - targetRegsReady ^= targetRegMask; + regNumber targetReg = genFirstRegNumFromMask(targetRegsReady); + targetRegsToDo.RemoveRegNumFromMask(targetReg); + targetRegsReady.RemoveRegNumFromMask(targetReg); assert(location[targetReg] != targetReg); assert(targetReg < REG_COUNT); regNumber sourceReg = (regNumber)source[targetReg]; @@ -9773,14 +9820,13 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, fromReg DEBUG_ARG(fromBlock) DEBUG_ARG(toBlock) DEBUG_ARG(resolveTypeName[resolveType])); sourceIntervals[sourceReg] = nullptr; location[sourceReg] = REG_NA; - regMaskTP fromRegMask = genRegMask(fromReg); // Do we have a free targetReg? if (fromReg == sourceReg) { - if (source[fromReg] != REG_NA && ((targetRegsFromStack & fromRegMask) != fromRegMask)) + if (source[fromReg] != REG_NA && !targetRegsFromStack.IsRegNumInMask(fromReg)) { - targetRegsReady |= fromRegMask; + targetRegsReady.AddRegNumInMask(fromReg); #ifdef TARGET_ARM if (genIsValidDoubleReg(fromReg)) { @@ -9791,7 +9837,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, regNumber upperHalfReg = REG_NEXT(fromReg); if ((otherInterval->registerType == TYP_DOUBLE) && (location[upperHalfReg] != REG_NA)) { - targetRegsReady &= ~fromRegMask; + targetRegsReady.RemoveRegNumFromMask(fromReg); } } } @@ -9813,19 +9859,19 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, // lowerHalfRegMask) if ((lowerHalfSrcReg != REG_NA) && (lowerHalfSrcLoc == REG_NA) && (sourceIntervals[lowerHalfSrcReg] != nullptr) && - ((targetRegsReady & lowerHalfRegMask) == RBM_NONE) && - ((targetRegsFromStack & lowerHalfRegMask) != lowerHalfRegMask)) + !targetRegsReady.IsRegNumInMask(lowerHalfReg) && + !targetRegsFromStack.IsRegNumInMask(lowerHalfReg)) { // This must be a double interval, otherwise it would be in targetRegsReady, or already // completed. assert(sourceIntervals[lowerHalfSrcReg]->registerType == TYP_DOUBLE); - targetRegsReady |= lowerHalfRegMask; + targetRegsReady.AddRegNumInMask(lowerHalfReg); } #endif // TARGET_ARM } } } - if (targetRegsToDo != RBM_NONE) + if (targetRegsToDo.IsNonEmpty()) { regNumber targetReg = genFirstRegNumFromMask(targetRegsToDo); regMaskTP targetRegMask = genRegMask(targetReg); @@ -9836,7 +9882,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, regNumber fromReg = (regNumber)location[sourceReg]; if (targetReg == fromReg) { - targetRegsToDo &= ~targetRegMask; + targetRegsToDo.RemoveRegNumFromMask(targetReg); } else { @@ -9879,8 +9925,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, // Otherwise, we'll spill it to the stack and reload it later. if (useSwap) { - regMaskTP fromRegMask = genRegMask(fromReg); - targetRegsToDo &= ~fromRegMask; + targetRegsToDo.RemoveRegNumFromMask(fromReg); } } else @@ -9888,7 +9933,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, // Look at the remaining registers from targetRegsToDo (which we expect to be relatively // small at this point) to find out what's currently in targetReg. regMaskTP mask = targetRegsToDo; - while (mask != RBM_NONE && otherTargetReg == REG_NA) + while (mask.IsNonEmpty() && otherTargetReg == REG_NA) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(mask); if (location[source[nextReg]] == targetReg) @@ -9927,7 +9972,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, regMaskTP otherTargetRegMask = genRegMask(otherTargetReg); targetRegsFromStack |= otherTargetRegMask; stackToRegIntervals[otherTargetReg] = otherInterval; - targetRegsToDo &= ~otherTargetRegMask; + targetRegsToDo.RemoveRegNumFromMask(otherTargetReg); // Now, move the interval that is going to targetReg. addResolution(block, insertionPoint, sourceIntervals[sourceReg], targetReg, @@ -9953,13 +9998,13 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, regNumber upperHalfReg = REG_NEXT(fromReg); if ((otherInterval->registerType == TYP_DOUBLE) && (location[upperHalfReg] != REG_NA)) { - targetRegsReady &= ~fromRegMask; + targetRegsReady.RemoveRegNumFromMask(fromReg); } } #endif // TARGET_ARM } } - targetRegsToDo &= ~targetRegMask; + targetRegsToDo.RemoveRegNumFromMask(targetReg); } else { @@ -9991,7 +10036,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, // Finally, perform stack to reg moves // All the target regs will be empty at this point - while (targetRegsFromStack != RBM_NONE) + while (targetRegsFromStack.IsNonEmpty()) { regNumber targetReg = genFirstRegNumFromMaskAndToggle(targetRegsFromStack); @@ -11064,7 +11109,7 @@ void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event, } if ((interval != nullptr) && (reg != REG_NA) && (reg != REG_STK)) { - registersToDump |= getRegMask(reg, interval->registerType); + registersToDump.AddRegNum(reg, interval->registerType); dumpRegRecordTitleIfNeeded(); } @@ -11423,7 +11468,7 @@ void LinearScan::dumpRegRecordTitleIfNeeded() int lastRegNumIndex = compiler->compFloatingPointUsed ? REG_FP_LAST : REG_INT_LAST; for (int regNumIndex = 0; regNumIndex <= lastRegNumIndex; regNumIndex++) { - if ((registersToDump & genRegMask((regNumber)regNumIndex)) != 0) + if (registersToDump.IsRegNumInMask((regNumber)regNumIndex)) { lastUsedRegNumIndex = regNumIndex; } @@ -11503,7 +11548,7 @@ void LinearScan::dumpRegRecords() #endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE printf("%c", activeChar); } - else if ((genRegMask(regNum) & regsBusyUntilKill) != RBM_NONE) + else if (regsBusyUntilKill.IsRegNumInMask(regNum)) { printf(columnFormatArray, "Busy"); } @@ -11740,7 +11785,7 @@ void LinearScan::verifyFreeRegisters(regMaskTP regsToFree) regMaskTP regMask = genRegMask(reg); // If this isn't available or if it's still waiting to be freed (i.e. it was in // delayRegsToFree and so now it's in regsToFree), then skip it. - if ((regMask & allAvailableRegs & ~regsToFree) == RBM_NONE) + if ((regMask & allAvailableRegs & ~regsToFree).IsEmpty()) { continue; } @@ -12182,7 +12227,7 @@ void LinearScan::verifyFinalAllocation() // However, we will assert that, at resolution time, no registers contain GC refs. { DBEXEC(VERBOSE, printf(" ")); - regMaskTP candidateRegs = currentRefPosition.registerAssignment; + SingleTypeRegSet candidateRegs = currentRefPosition.registerAssignment; while (candidateRegs != RBM_NONE) { regNumber nextReg = genFirstRegNumFromMaskAndToggle(candidateRegs); @@ -12830,7 +12875,7 @@ void LinearScan::RegisterSelection::try_BEST_FIT() for (SingleTypeRegSet bestFitCandidates = candidates; bestFitCandidates != RBM_NONE;) { regNumber bestFitCandidateRegNum = genFirstRegNumFromMask(bestFitCandidates); - SingleTypeRegSet bestFitCandidateBit = genRegMask(bestFitCandidateRegNum); + SingleTypeRegSet bestFitCandidateBit = genSingleTypeRegMask(bestFitCandidateRegNum); bestFitCandidates ^= bestFitCandidateBit; // Find the next RefPosition of the register. @@ -12929,7 +12974,7 @@ void LinearScan::RegisterSelection::try_REG_ORDER() for (SingleTypeRegSet regOrderCandidates = candidates; regOrderCandidates != RBM_NONE;) { regNumber regOrderCandidateRegNum = genFirstRegNumFromMask(regOrderCandidates); - SingleTypeRegSet regOrderCandidateBit = genRegMask(regOrderCandidateRegNum); + SingleTypeRegSet regOrderCandidateBit = genSingleTypeRegMask(regOrderCandidateRegNum); regOrderCandidates ^= regOrderCandidateBit; unsigned thisRegOrder = linearScan->getRegisterRecord(regOrderCandidateRegNum)->regOrder; @@ -12965,7 +13010,7 @@ void LinearScan::RegisterSelection::try_SPILL_COST() for (SingleTypeRegSet spillCandidates = candidates; spillCandidates != RBM_NONE;) { regNumber spillCandidateRegNum = genFirstRegNumFromMask(spillCandidates); - SingleTypeRegSet spillCandidateBit = genRegMask(spillCandidateRegNum); + SingleTypeRegSet spillCandidateBit = genSingleTypeRegMask(spillCandidateRegNum); spillCandidates ^= spillCandidateBit; RegRecord* spillCandidateRegRecord = &linearScan->physRegs[spillCandidateRegNum]; @@ -13090,7 +13135,7 @@ void LinearScan::RegisterSelection::try_FAR_NEXT_REF() for (SingleTypeRegSet farthestCandidates = candidates; farthestCandidates != RBM_NONE;) { regNumber farthestCandidateRegNum = genFirstRegNumFromMask(farthestCandidates); - SingleTypeRegSet farthestCandidateBit = genRegMask(farthestCandidateRegNum); + SingleTypeRegSet farthestCandidateBit = genSingleTypeRegMask(farthestCandidateRegNum); farthestCandidates ^= farthestCandidateBit; // Find the next RefPosition of the register. @@ -13123,7 +13168,7 @@ void LinearScan::RegisterSelection::try_PREV_REG_OPT() for (SingleTypeRegSet prevRegOptCandidates = candidates; prevRegOptCandidates != RBM_NONE;) { regNumber prevRegOptCandidateRegNum = genFirstRegNumFromMask(prevRegOptCandidates); - SingleTypeRegSet prevRegOptCandidateBit = genRegMask(prevRegOptCandidateRegNum); + SingleTypeRegSet prevRegOptCandidateBit = genSingleTypeRegMask(prevRegOptCandidateRegNum); prevRegOptCandidates ^= prevRegOptCandidateBit; Interval* assignedInterval = linearScan->physRegs[prevRegOptCandidateRegNum].assignedInterval; bool foundPrevRegOptReg = true; @@ -13226,7 +13271,7 @@ void LinearScan::RegisterSelection::calculateUnassignedSets() // TODO: Seperate for (; coversCandidates != RBM_NONE;) { regNumber coversCandidateRegNum = genFirstRegNumFromMask(coversCandidates); - SingleTypeRegSet coversCandidateBit = genRegMask(coversCandidateRegNum); + SingleTypeRegSet coversCandidateBit = genSingleTypeRegMask(coversCandidateRegNum); coversCandidates ^= coversCandidateBit; // The register is considered unassigned if it has no assignedInterval, OR @@ -13254,7 +13299,7 @@ void LinearScan::RegisterSelection::calculateCoversSets() for (; coversCandidates != RBM_NONE;) { regNumber coversCandidateRegNum = genFirstRegNumFromMask(coversCandidates); - SingleTypeRegSet coversCandidateBit = genRegMask(coversCandidateRegNum); + SingleTypeRegSet coversCandidateBit = genSingleTypeRegMask(coversCandidateRegNum); coversCandidates ^= coversCandidateBit; // If we have a single candidate we don't need to compute the preference-related sets, but we @@ -13425,8 +13470,8 @@ SingleTypeRegSet LinearScan::RegisterSelection::select(Interval* // to achieve zero diffs. // bool thisIsSingleReg = isSingleRegister(newRelatedPreferences); - if (!thisIsSingleReg || - linearScan->isFree(linearScan->getRegisterRecord(genRegNumFromMask(newRelatedPreferences)))) + if (!thisIsSingleReg || linearScan->isFree(linearScan->getRegisterRecord( + genRegNumFromMask(newRelatedPreferences, regType)))) { relatedPreferences = newRelatedPreferences; // If this Interval has a downstream def without a single-register preference, continue to iterate. @@ -13521,7 +13566,7 @@ SingleTypeRegSet LinearScan::RegisterSelection::select(Interval* if (candidates == refPosition->registerAssignment) { found = true; - if (linearScan->nextIntervalRef[genRegNumFromMask(candidates)] > lastLocation) + if (linearScan->nextIntervalRef[genRegNumFromMask(candidates, regType)] > lastLocation) { unassignedSet = candidates; } @@ -13565,7 +13610,7 @@ SingleTypeRegSet LinearScan::RegisterSelection::select(Interval* while (checkConflictMask != RBM_NONE) { regNumber checkConflictReg = genFirstRegNumFromMask(checkConflictMask); - SingleTypeRegSet checkConflictBit = genRegMask(checkConflictReg); + SingleTypeRegSet checkConflictBit = genSingleTypeRegMask(checkConflictReg); checkConflictMask ^= checkConflictBit; LsraLocation checkConflictLocation = linearScan->nextFixedRef[checkConflictReg]; @@ -13590,7 +13635,7 @@ SingleTypeRegSet LinearScan::RegisterSelection::select(Interval* if (!found && (currentInterval->assignedReg != nullptr)) { RegRecord* prevRegRec = currentInterval->assignedReg; - prevRegBit = genRegMask(prevRegRec->regNum); + prevRegBit = genSingleTypeRegMask(prevRegRec->regNum); if ((prevRegRec->assignedInterval == currentInterval) && ((candidates & prevRegBit) != RBM_NONE)) { if (!needsConsecutiveRegisters) @@ -13658,7 +13703,7 @@ SingleTypeRegSet LinearScan::RegisterSelection::select(Interval* SingleTypeRegSet limitCandidatesForConsecutive = ((refPosition->registerAssignment & ~inUseOrBusyRegsMask) & linearScan->availableFloatRegs); SingleTypeRegSet overallLimitCandidates; - regMaskTP limitConsecutiveResult = + SingleTypeRegSet limitConsecutiveResult = linearScan->filterConsecutiveCandidates(limitCandidatesForConsecutive, refPosition->regCount, &overallLimitCandidates); assert(limitConsecutiveResult != RBM_NONE); @@ -13884,7 +13929,7 @@ SingleTypeRegSet LinearScan::RegisterSelection::selectMinimal( while (checkConflictMask != RBM_NONE) { regNumber checkConflictReg = genFirstRegNumFromMask(checkConflictMask); - SingleTypeRegSet checkConflictBit = genRegMask(checkConflictReg); + SingleTypeRegSet checkConflictBit = genSingleTypeRegMask(checkConflictReg); checkConflictMask ^= checkConflictBit; LsraLocation checkConflictLocation = linearScan->nextFixedRef[checkConflictReg]; diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index e20a92a695426..249d60e051dcf 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -451,11 +451,12 @@ typedef jitstd::list::reverse_iterator RefPositionReverseIterator; class Referenceable { public: - Referenceable() + Referenceable(RegisterType _registerType) { firstRefPosition = nullptr; recentRefPosition = nullptr; lastRefPosition = nullptr; + registerType = _registerType; } // A linked list of RefPositions. These are only traversed in the forward @@ -466,6 +467,8 @@ class Referenceable RefPosition* recentRefPosition; RefPosition* lastRefPosition; + RegisterType registerType; + // Get the position of the next reference which is at or greater than // the current location (relies upon recentRefPosition being updated // during traversal). @@ -477,12 +480,12 @@ class RegRecord : public Referenceable { public: RegRecord() + : Referenceable(IntRegisterType) { assignedInterval = nullptr; previousInterval = nullptr; regNum = REG_NA; isCalleeSave = false; - registerType = IntRegisterType; } void init(regNumber reg) @@ -537,7 +540,6 @@ class RegRecord : public Referenceable regNumber regNum; bool isCalleeSave; - RegisterType registerType; unsigned char regOrder; }; @@ -622,7 +624,7 @@ class LinearScan : public LinearScanInterface // This is the main driver virtual PhaseStatus doLinearScan(); - static bool isSingleRegister(regMaskTP regMask) + static bool isSingleRegister(SingleTypeRegSet regMask) { return (genExactlyOneBit(regMask)); } @@ -1243,18 +1245,19 @@ class LinearScan : public LinearScanInterface unsigned int registersNeeded); #endif // TARGET_ARM64 - SingleTypeRegSet getFreeCandidates(regMaskTP candidates, var_types regType) + SingleTypeRegSet getFreeCandidates(SingleTypeRegSet candidates, var_types regType) { - regMaskTP result = candidates & m_AvailableRegs; + SingleTypeRegSet availableRegsForType = m_AvailableRegs.GetRegSetForType(regType); + SingleTypeRegSet result = candidates & availableRegsForType; #ifdef TARGET_ARM // For TYP_DOUBLE on ARM, we can only use register for which the odd half is // also available. if (regType == TYP_DOUBLE) { - result &= (m_AvailableRegs >> 1); + result &= (availableRegsForType >> 1); } #endif // TARGET_ARM - return result.GetRegSetForType(regType); + return result; } #ifdef DEBUG @@ -1500,7 +1503,7 @@ class LinearScan : public LinearScanInterface int lastUsedRegNumIndex; bool shouldDumpReg(regNumber regNum) { - return (registersToDump & genRegMask(regNum)) != 0; + return registersToDump.IsRegNumInMask(regNum); } void dumpRegRecordHeader(); @@ -1722,10 +1725,12 @@ class LinearScan : public LinearScanInterface #endif PhasedVar* availableRegs[TYP_COUNT]; -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) -#define allAvailableRegs (availableIntRegs | availableFloatRegs | availableMaskRegs) +#if defined(TARGET_XARCH) +#define allAvailableRegs regMaskTP(availableIntRegs | availableFloatRegs | availableMaskRegs) +#elif defined(TARGET_ARM64) +#define allAvailableRegs regMaskTP(availableIntRegs | availableFloatRegs, availableMaskRegs) #else -#define allAvailableRegs (availableIntRegs | availableFloatRegs) +#define allAvailableRegs regMaskTP(availableIntRegs | availableFloatRegs) #endif // Register mask of argument registers currently occupied because we saw a @@ -1782,30 +1787,6 @@ class LinearScan : public LinearScanInterface //----------------------------------------------------------------------- regMaskTP m_AvailableRegs; - regNumber getRegForType(regNumber reg, var_types regType) - { -#ifdef TARGET_ARM - if ((regType == TYP_DOUBLE) && !genIsValidDoubleReg(reg)) - { - reg = REG_PREV(reg); - } -#endif // TARGET_ARM - return reg; - } - - regMaskTP getRegMask(regNumber reg, var_types regType) - { - reg = getRegForType(reg, regType); - regMaskTP regMask = genRegMask(reg); -#ifdef TARGET_ARM - if (regType == TYP_DOUBLE) - { - assert(genIsValidDoubleReg(reg)); - regMask |= (regMask << 1); - } -#endif // TARGET_ARM - return regMask; - } void resetAvailableRegs() { @@ -1815,8 +1796,7 @@ class LinearScan : public LinearScanInterface bool isRegAvailable(regNumber reg, var_types regType) { - regMaskTP regMask = getRegMask(reg, regType); - return (m_AvailableRegs & regMask) == regMask; + return m_AvailableRegs.IsRegNumPresent(reg, regType); } void setRegsInUse(regMaskTP regMask) { @@ -1824,8 +1804,7 @@ class LinearScan : public LinearScanInterface } void setRegInUse(regNumber reg, var_types regType) { - regMaskTP regMask = getRegMask(reg, regType); - setRegsInUse(regMask); + m_AvailableRegs.RemoveRegNum(reg, regType); } void makeRegsAvailable(regMaskTP regMask) { @@ -1833,8 +1812,7 @@ class LinearScan : public LinearScanInterface } void makeRegAvailable(regNumber reg, var_types regType) { - regMaskTP regMask = getRegMask(reg, regType); - makeRegsAvailable(regMask); + m_AvailableRegs.AddRegNum(reg, regType); } void clearAllNextIntervalRef(); @@ -1846,7 +1824,8 @@ class LinearScan : public LinearScanInterface void updateSpillCost(regNumber reg, Interval* interval); FORCEINLINE void updateRegsFreeBusyState(RefPosition& refPosition, - regMaskTP regsBusy, + var_types registerType, + SingleTypeRegSet regsBusy, regMaskTP* regsToFree, regMaskTP* delayRegsToFree DEBUG_ARG(Interval* interval) DEBUG_ARG(regNumber assignedReg)); @@ -1854,17 +1833,15 @@ class LinearScan : public LinearScanInterface regMaskTP m_RegistersWithConstants; void clearConstantReg(regNumber reg, var_types regType) { - m_RegistersWithConstants &= ~getRegMask(reg, regType); + m_RegistersWithConstants.RemoveRegNum(reg, regType); } void setConstantReg(regNumber reg, var_types regType) { - m_RegistersWithConstants |= getRegMask(reg, regType); + m_RegistersWithConstants.AddRegNum(reg, regType); } bool isRegConstant(regNumber reg, var_types regType) { - reg = getRegForType(reg, regType); - regMaskTP regMask = getRegMask(reg, regType); - return (m_RegistersWithConstants & regMask) == regMask; + return m_RegistersWithConstants.IsRegNumPresent(reg, regType); } SingleTypeRegSet getMatchingConstants(SingleTypeRegSet mask, Interval* currentInterval, RefPosition* refPosition); @@ -1905,22 +1882,20 @@ class LinearScan : public LinearScanInterface #endif bool isRegBusy(regNumber reg, var_types regType) { - regMaskTP regMask = getRegMask(reg, regType); - return (regsBusyUntilKill & regMask) != RBM_NONE; + return regsBusyUntilKill.IsRegNumPresent(reg, regType); } void setRegBusyUntilKill(regNumber reg, var_types regType) { - regsBusyUntilKill |= getRegMask(reg, regType); + regsBusyUntilKill.AddRegNum(reg, regType); } void clearRegBusyUntilKill(regNumber reg) { - regsBusyUntilKill &= ~genRegMask(reg); + regsBusyUntilKill.RemoveRegNumFromMask(reg); } bool isRegInUse(regNumber reg, var_types regType) { - regMaskTP regMask = getRegMask(reg, regType); - return (regsInUseThisLocation & regMask) != RBM_NONE; + return regsInUseThisLocation.IsRegNumPresent(reg, regType); } void resetRegState() @@ -2097,28 +2072,28 @@ class LinearScan : public LinearScanInterface int BuildLclHeap(GenTree* tree); #if defined(TARGET_AMD64) - SingleTypeRegSet rbmAllFloat; - SingleTypeRegSet rbmFltCalleeTrash; + regMaskTP rbmAllFloat; + regMaskTP rbmFltCalleeTrash; - FORCEINLINE SingleTypeRegSet get_RBM_ALLFLOAT() const + FORCEINLINE regMaskTP get_RBM_ALLFLOAT() const { return this->rbmAllFloat; } - FORCEINLINE SingleTypeRegSet get_RBM_FLT_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_FLT_CALLEE_TRASH() const { return this->rbmFltCalleeTrash; } #endif // TARGET_AMD64 #if defined(TARGET_XARCH) - SingleTypeRegSet rbmAllMask; - SingleTypeRegSet rbmMskCalleeTrash; + regMaskTP rbmAllMask; + regMaskTP rbmMskCalleeTrash; - FORCEINLINE SingleTypeRegSet get_RBM_ALLMASK() const + FORCEINLINE regMaskTP get_RBM_ALLMASK() const { return this->rbmAllMask; } - FORCEINLINE SingleTypeRegSet get_RBM_MSK_CALLEE_TRASH() const + FORCEINLINE regMaskTP get_RBM_MSK_CALLEE_TRASH() const { return this->rbmMskCalleeTrash; } @@ -2139,21 +2114,21 @@ class LinearScan : public LinearScanInterface // static FORCEINLINE SingleTypeRegSet calleeSaveRegs(RegisterType rt) { - static const SingleTypeRegSet varTypeCalleeSaveRegs[] = { + static const regMaskTP varTypeCalleeSaveRegs[] = { #define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, csr, ctr, tf) csr, #include "typelist.h" #undef DEF_TP }; assert((unsigned)rt < ArrLen(varTypeCalleeSaveRegs)); - return varTypeCalleeSaveRegs[rt]; + return varTypeCalleeSaveRegs[rt].GetRegSetForType(rt); } #if defined(TARGET_XARCH) // Not all of the callee trash values are constant, so don't declare this as a method local static // doing so results in significantly more complex codegen and we'd rather just initialize this once // as part of initializing LSRA instead - SingleTypeRegSet varTypeCalleeTrashRegs[TYP_COUNT]; + regMaskTP varTypeCalleeTrashRegs[TYP_COUNT]; #endif // TARGET_XARCH //------------------------------------------------------------------------ @@ -2162,7 +2137,7 @@ class LinearScan : public LinearScanInterface FORCEINLINE SingleTypeRegSet callerSaveRegs(RegisterType rt) const { #if !defined(TARGET_XARCH) - static const SingleTypeRegSet varTypeCalleeTrashRegs[] = { + static const regMaskTP varTypeCalleeTrashRegs[] = { #define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, csr, ctr, tf) ctr, #include "typelist.h" #undef DEF_TP @@ -2170,7 +2145,7 @@ class LinearScan : public LinearScanInterface #endif // !TARGET_XARCH assert((unsigned)rt < ArrLen(varTypeCalleeTrashRegs)); - return varTypeCalleeTrashRegs[rt]; + return varTypeCalleeTrashRegs[rt].GetRegSetForType(rt); } }; @@ -2189,14 +2164,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX class Interval : public Referenceable { public: - Interval(RegisterType registerType, regMaskTP registerPreferences) - : registerPreferences(registerPreferences) + Interval(RegisterType registerType, SingleTypeRegSet registerPreferences) + : Referenceable(registerType) + , registerPreferences(registerPreferences) , registerAversion(RBM_NONE) , relatedInterval(nullptr) , assignedReg(nullptr) , varNum(0) , physReg(REG_COUNT) - , registerType(registerType) , isActive(false) , isLocalVar(false) , isSplit(false) @@ -2253,8 +2228,6 @@ class Interval : public Referenceable // The register to which it is currently assigned. regNumber physReg; - RegisterType registerType; - // Is this Interval currently in a register and live? bool isActive; @@ -2338,7 +2311,8 @@ class Interval : public Referenceable { // This uses regMasks to handle the case where a double actually occupies two registers // TODO-Throughput: This could/should be done more cheaply. - return (physReg != REG_NA && (genRegMask(physReg, registerType) & genRegMask(regNum)) != RBM_NONE); + return (physReg != REG_NA && + (genSingleTypeRegMask(physReg, registerType) & genSingleTypeRegMask(regNum)) != RBM_NONE); } // Assign the related interval. @@ -2387,7 +2361,7 @@ class Interval : public Referenceable // SingleTypeRegSet getCurrentPreferences() { - return (assignedReg == nullptr) ? registerPreferences : genRegMask(assignedReg->regNum); + return (assignedReg == nullptr) ? registerPreferences : genSingleTypeRegMask(assignedReg->regNum); } void mergeRegisterPreferences(SingleTypeRegSet preferences) @@ -2659,7 +2633,7 @@ class RefPosition { referent = r; isPhysRegRef = true; - registerAssignment = genRegMask(r->regNum); + registerAssignment = genSingleTypeRegMask(r->regNum); } regNumber assignedReg() @@ -2669,7 +2643,12 @@ class RefPosition return REG_NA; } - return genRegNumFromMask(registerAssignment); + return genRegNumFromMask(registerAssignment, getRegisterType()); + } + + RegisterType getRegisterType() + { + return referent->registerType; } // Returns true if it is a reference on a GenTree node. @@ -2760,7 +2739,7 @@ class RefPosition // isFixedRefOfRegMask indicates that the RefPosition has a fixed assignment to the register // specified by the given mask - bool isFixedRefOfRegMask(regMaskTP regMask) + bool isFixedRefOfRegMask(SingleTypeRegSet regMask) { assert(genMaxOneBit(regMask)); return (registerAssignment == regMask); @@ -2769,7 +2748,7 @@ class RefPosition // isFixedRefOfReg indicates that the RefPosition has a fixed assignment to the given register bool isFixedRefOfReg(regNumber regNum) { - return (isFixedRefOfRegMask(genRegMask(regNum))); + return (isFixedRefOfRegMask(genSingleTypeRegMask(regNum))); } #ifdef TARGET_ARM64 diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index f2c60cde13eb0..fc77279eabb75 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -670,7 +670,7 @@ int LinearScan::BuildNode(GenTree* tree) SingleTypeRegSet argMask = RBM_NONE; if (argReg != REG_COUNT) { - argMask = genRegMask(argReg); + argMask = genSingleTypeRegMask(argReg); } // If type of node is `long` then it is actually `double`. @@ -679,7 +679,7 @@ int LinearScan::BuildNode(GenTree* tree) { dstCount++; assert(genRegArgNext(argReg) == REG_NEXT(argReg)); - argMask |= genRegMask(REG_NEXT(argReg)); + argMask |= genSingleTypeRegMask(REG_NEXT(argReg)); dstCount = 2; } if (!tree->gtGetOp1()->isContained()) diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 5283d2fc00fff..53c543291671e 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -103,7 +103,7 @@ void LinearScan::assignConsecutiveRegisters(RefPosition* firstRefPosition, regNu #endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE INDEBUG(refPosCount++); assert((consecutiveRefPosition->refType == RefTypeDef) || (consecutiveRefPosition->refType == RefTypeUse)); - consecutiveRefPosition->registerAssignment = genRegMask(regToAssign); + consecutiveRefPosition->registerAssignment = genSingleTypeRegMask(regToAssign); consecutiveRefPosition = getNextConsecutiveRefPosition(consecutiveRefPosition); regToAssign = regToAssign == REG_FP_LAST ? REG_FP_FIRST : REG_NEXT(regToAssign); } @@ -333,16 +333,17 @@ SingleTypeRegSet LinearScan::filterConsecutiveCandidatesForSpill(SingleTypeRegSe SingleTypeRegSet consecutiveResultForBusy = RBM_NONE; SingleTypeRegSet unprocessedRegs = consecutiveCandidates; unsigned regAvailableStartIndex = 0, regAvailableEndIndex = 0; - int maxSpillRegs = registersNeeded; - SingleTypeRegSet registersNeededMask = (1ULL << registersNeeded) - 1; + int maxSpillRegs = registersNeeded; + SingleTypeRegSet registersNeededMask = (1ULL << registersNeeded) - 1; + SingleTypeRegSet availableFloatRegSet = m_AvailableRegs.GetFloatRegSet(); do { // From LSB, find the first available register (bit `1`) regAvailableStartIndex = BitScanForward(unprocessedRegs); // For the current range, find how many registers are free vs. busy - regMaskTP maskForCurRange = RBM_NONE; - bool shouldCheckForRounding = false; + SingleTypeRegSet maskForCurRange = RBM_NONE; + bool shouldCheckForRounding = false; switch (registersNeeded) { case 2: @@ -366,7 +367,7 @@ SingleTypeRegSet LinearScan::filterConsecutiveCandidatesForSpill(SingleTypeRegSe } maskForCurRange |= (registersNeededMask << regAvailableStartIndex); - maskForCurRange &= m_AvailableRegs; + maskForCurRange &= availableFloatRegSet; if (maskForCurRange != RBM_NONE) { @@ -422,9 +423,7 @@ SingleTypeRegSet LinearScan::getConsecutiveCandidates(SingleTypeRegSet allCandi { assert(compiler->info.compNeedsConsecutiveRegisters); assert(refPosition->isFirstRefPositionOfConsecutiveRegisters()); - regMaskTP freeCandidates = allCandidates & m_AvailableRegs; - assert((freeCandidates.IsEmpty()) || (freeCandidates.getLow() & availableFloatRegs)); - SingleTypeRegSet floatFreeCandidates = freeCandidates.getLow(); + SingleTypeRegSet floatFreeCandidates = allCandidates & m_AvailableRegs.GetFloatRegSet(); #ifdef DEBUG if (getStressLimitRegs() != LSRA_LIMIT_NONE) @@ -489,7 +488,8 @@ SingleTypeRegSet LinearScan::getConsecutiveCandidates(SingleTypeRegSet allCandi if (foundCount != 0) { assert(firstRegNum != REG_NA); - regMaskTP remainingRegsMask = ((1ULL << (registersNeeded - foundCount)) - 1) << (firstRegNum - 1); + SingleTypeRegSet remainingRegsMask = ((1ULL << (registersNeeded - foundCount)) - 1) + << (firstRegNum - 1); if ((overallResult & remainingRegsMask) != RBM_NONE) { @@ -536,7 +536,7 @@ SingleTypeRegSet LinearScan::getConsecutiveCandidates(SingleTypeRegSet allCandi *busyCandidates = consecutiveResultForBusy; // Check if we can further check better registers amoung consecutiveResultForBusy. - if ((m_AvailableRegs & overallResultForBusy) != RBM_NONE) + if ((m_AvailableRegs.GetFloatRegSet() & overallResultForBusy) != RBM_NONE) { // `overallResultForBusy` contains the mask of entire series that can be the consecutive candidates. // If there is an overlap of that with free registers, then try to find a series that will need least @@ -549,13 +549,13 @@ SingleTypeRegSet LinearScan::getConsecutiveCandidates(SingleTypeRegSet allCandi { *busyCandidates = optimalConsecutiveResultForBusy; } - else if ((m_AvailableRegs & consecutiveResultForBusy) != RBM_NONE) + else if ((m_AvailableRegs.GetFloatRegSet() & consecutiveResultForBusy) != RBM_NONE) { // We did not find free consecutive candidates, however we found some registers among the // `allCandidates` that are mix of free and busy. Since `busyCandidates` just has bit set for first // register of such series, return the mask that starts with free register, if possible. The busy // registers will be spilled during assignment of subsequent RefPosition. - *busyCandidates = (m_AvailableRegs.GetRegSetForType(TYP_FLOAT) & consecutiveResultForBusy); + *busyCandidates = (m_AvailableRegs.GetFloatRegSet() & consecutiveResultForBusy); } } @@ -1472,6 +1472,27 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou needBranchTargetReg = !intrin.op1->isContainedIntOrIImmed(); break; + case NI_Sve_SaturatingDecrementBy16BitElementCount: + case NI_Sve_SaturatingDecrementBy32BitElementCount: + case NI_Sve_SaturatingDecrementBy64BitElementCount: + case NI_Sve_SaturatingDecrementBy8BitElementCount: + case NI_Sve_SaturatingIncrementBy16BitElementCount: + case NI_Sve_SaturatingIncrementBy32BitElementCount: + case NI_Sve_SaturatingIncrementBy64BitElementCount: + case NI_Sve_SaturatingIncrementBy8BitElementCount: + case NI_Sve_SaturatingDecrementBy16BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy32BitElementCountScalar: + case NI_Sve_SaturatingDecrementBy64BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy16BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy32BitElementCountScalar: + case NI_Sve_SaturatingIncrementBy64BitElementCountScalar: + // Can only avoid generating a table if both immediates are constant. + assert(intrin.op2->isContainedIntOrIImmed() == intrin.op3->isContainedIntOrIImmed()); + needBranchTargetReg = !intrin.op2->isContainedIntOrIImmed(); + // Ensure that internal does not collide with desination. + setInternalRegsDelayFree = true; + break; + default: unreached(); } @@ -1781,6 +1802,19 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou break; } + case NI_Sve_Load2xVectorAndUnzip: + case NI_Sve_Load3xVectorAndUnzip: + case NI_Sve_Load4xVectorAndUnzip: + { + assert(intrin.op1 != nullptr); + assert(intrin.op2 != nullptr); + assert(intrinsicTree->OperIsMemoryLoadOrStore()); + srcCount += BuildAddrUses(intrin.op2); + BuildConsecutiveRegistersForDef(intrinsicTree, dstCount); + *pDstCount = dstCount; + break; + } + case NI_Sve_StoreAndZipx2: case NI_Sve_StoreAndZipx3: case NI_Sve_StoreAndZipx4: @@ -1938,6 +1972,13 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou else { SingleTypeRegSet candidates = lowVectorOperandNum == 2 ? lowVectorCandidates : RBM_NONE; + + if (intrin.op2->gtType == TYP_MASK) + { + assert(lowVectorOperandNum != 2); + candidates = RBM_ALLMASK; + } + if (forceOp2DelayFree) { srcCount += BuildDelayFreeUses(intrin.op2, nullptr, candidates); diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index 85f1f66442404..323dea8d4809a 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -174,7 +174,8 @@ int LinearScan::BuildCall(GenTreeCall* call) ctrlExprCandidates = allRegs(TYP_INT) & RBM_INT_CALLEE_TRASH & ~RBM_LR; if (compiler->getNeedsGSSecurityCookie()) { - ctrlExprCandidates &= ~(genRegMask(REG_GSCOOKIE_TMP_0) | genRegMask(REG_GSCOOKIE_TMP_1)); + ctrlExprCandidates &= + ~(genSingleTypeRegMask(REG_GSCOOKIE_TMP_0) | genSingleTypeRegMask(REG_GSCOOKIE_TMP_1)); } assert(ctrlExprCandidates != RBM_NONE); } @@ -291,7 +292,7 @@ int LinearScan::BuildCall(GenTreeCall* call) } #endif // TARGET_ARM #endif - BuildUse(use.GetNode(), genRegMask(use.GetNode()->GetRegNum())); + BuildUse(use.GetNode(), genSingleTypeRegMask(use.GetNode()->GetRegNum())); srcCount++; } } @@ -301,7 +302,7 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(regCount == abiInfo.NumRegs); for (unsigned int i = 0; i < regCount; i++) { - BuildUse(argNode, genRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); + BuildUse(argNode, genSingleTypeRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); } srcCount += regCount; } @@ -317,14 +318,14 @@ int LinearScan::BuildCall(GenTreeCall* call) if (argNode->TypeGet() == TYP_LONG) { assert(argNode->IsMultiRegNode()); - BuildUse(argNode, genRegMask(argNode->GetRegNum()), 0); - BuildUse(argNode, genRegMask(genRegArgNext(argNode->GetRegNum())), 1); + BuildUse(argNode, genSingleTypeRegMask(argNode->GetRegNum()), 0); + BuildUse(argNode, genSingleTypeRegMask(genRegArgNext(argNode->GetRegNum())), 1); srcCount += 2; } else #endif // TARGET_ARM { - BuildUse(argNode, genRegMask(argNode->GetRegNum())); + BuildUse(argNode, genSingleTypeRegMask(argNode->GetRegNum())); srcCount++; } } @@ -384,9 +385,9 @@ int LinearScan::BuildCall(GenTreeCall* call) // that we will attach to this node to guarantee that they are available // during generating this node. assert(call->gtFlags & GTF_TLS_GET_ADDR); - newRefPosition(REG_R0, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R0)); - newRefPosition(REG_R1, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R1)); - ctrlExprCandidates = genRegMask(REG_R2); + newRefPosition(REG_R0, currentLoc, RefTypeFixedReg, nullptr, genSingleTypeRegMask(REG_R0)); + newRefPosition(REG_R1, currentLoc, RefTypeFixedReg, nullptr, genSingleTypeRegMask(REG_R1)); + ctrlExprCandidates = genSingleTypeRegMask(REG_R2); } #endif BuildUse(ctrlExpr, ctrlExprCandidates); @@ -541,7 +542,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) for (unsigned i = 0; i < argNode->gtNumRegs; i++) { regNumber thisArgReg = (regNumber)((unsigned)argReg + i); - argMask |= genRegMask(thisArgReg); + argMask |= genSingleTypeRegMask(thisArgReg); argNode->SetRegNumByIdx(thisArgReg, i); } assert((argMask == RBM_NONE) || ((argMask & availableIntRegs) != RBM_NONE) || @@ -582,7 +583,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) SingleTypeRegSet sourceMask = RBM_NONE; if (sourceRegCount < argNode->gtNumRegs) { - sourceMask = genRegMask((regNumber)((unsigned)argReg + sourceRegCount)); + sourceMask = genSingleTypeRegMask((regNumber)((unsigned)argReg + sourceRegCount)); } sourceRegCount++; BuildUse(node, sourceMask, regIndex); diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 3be436c53b018..e130d9fc600cf 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -600,7 +600,7 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval, if (insertFixedRef) { - regNumber physicalReg = genRegNumFromMask(mask); + regNumber physicalReg = genRegNumFromMask(mask, theInterval->registerType); RefPosition* pos = newRefPosition(physicalReg, theLocation, RefTypeFixedReg, nullptr, mask); assert(theInterval != nullptr); #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -851,15 +851,23 @@ regMaskTP LinearScan::getKillSetForCall(GenTreeCall* call) if (!compiler->compFloatingPointUsed) { #if defined(TARGET_XARCH) - killMask &= ~(RBM_FLT_CALLEE_TRASH | RBM_MSK_CALLEE_TRASH); + +#ifdef TARGET_AMD64 + killMask.RemoveRegsetForType(RBM_FLT_CALLEE_TRASH.getLow(), FloatRegisterType); + killMask.RemoveRegsetForType(RBM_MSK_CALLEE_TRASH.getLow(), MaskRegisterType); +#else + killMask.RemoveRegsetForType(RBM_FLT_CALLEE_TRASH, FloatRegisterType); + killMask &= ~RBM_MSK_CALLEE_TRASH; +#endif // TARGET_AMD64 + #else - killMask &= ~RBM_FLT_CALLEE_TRASH; + killMask.RemoveRegsetForType(RBM_FLT_CALLEE_TRASH, FloatRegisterType); #endif // TARGET_XARCH } #ifdef TARGET_ARM if (call->IsVirtualStub()) { - killMask |= compiler->virtualStubParamInfo->GetRegMask(); + killMask.AddGprRegs(compiler->virtualStubParamInfo->GetRegMask()); } #else // !TARGET_ARM // Verify that the special virtual stub call registers are in the kill mask. @@ -875,7 +883,7 @@ regMaskTP LinearScan::getKillSetForCall(GenTreeCall* call) // so don't use the register post-call until it is consumed by SwiftError. if (call->HasSwiftErrorHandling()) { - killMask |= RBM_SWIFT_ERROR; + killMask.AddGprRegs(RBM_SWIFT_ERROR); } #endif // SWIFT_SUPPORT @@ -911,7 +919,7 @@ regMaskTP LinearScan::getKillSetForBlockStore(GenTreeBlk* blkNode) if (isCopyBlk) { // rep movs kills RCX, RDI and RSI - killMask = RBM_RCX | RBM_RDI | RBM_RSI; + killMask.AddGprRegs(RBM_RCX | RBM_RDI | RBM_RSI); } else { @@ -919,7 +927,7 @@ regMaskTP LinearScan::getKillSetForBlockStore(GenTreeBlk* blkNode) // (Note that the Data() node, if not constant, will be assigned to // RCX, but it's find that this kills it, as the value is not available // after this node in any case.) - killMask = RBM_RDI | RBM_RCX; + killMask.AddGprRegs(RBM_RDI | RBM_RCX); } break; #endif @@ -1113,7 +1121,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo { bool insertedKills = false; - if (killMask != RBM_NONE) + if (killMask.IsNonEmpty()) { addKillForRegs(killMask, currentLoc); @@ -1847,10 +1855,7 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc // {eax,edx} are getting killed before the def of GT_DIV. For this reason, minRegCount for // the use position of v02 also needs to take into account the kill set of its consuming node. regMaskTP killMask = getKillSetForNode(tree); - if (killMask != RBM_NONE) - { - minRegCountForRef += genCountBits(killMask); - } + minRegCountForRef += genCountBits(killMask); } else if ((newRefPosition->refType) == RefTypeDef && (newRefPosition->getInterval()->isSpecialPutArg)) { @@ -2117,12 +2122,12 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc) if ((genRegMask(argDsc->GetArgReg()) & RBM_ALLFLOAT) != RBM_NONE) { assert((genRegMask(argDsc->GetArgReg()) & RBM_FLTARG_REGS) != RBM_NONE); - floatRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg()); + floatRegState->rsCalleeRegArgMaskLiveIn.AddRegNumInMask(argDsc->GetArgReg()); } else { assert((genRegMask(argDsc->GetArgReg()) & fullIntArgRegMask(compiler->info.compCallConv)) != RBM_NONE); - intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetArgReg()); + intRegState->rsCalleeRegArgMaskLiveIn.AddRegNumInMask(argDsc->GetArgReg()); } } @@ -2131,12 +2136,12 @@ void LinearScan::UpdateRegStateForStructArg(LclVarDsc* argDsc) if (genRegMask(argDsc->GetOtherArgReg()) & (RBM_ALLFLOAT)) { assert(genRegMask(argDsc->GetOtherArgReg()) & (RBM_FLTARG_REGS)); - floatRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetOtherArgReg()); + floatRegState->rsCalleeRegArgMaskLiveIn.AddRegNumInMask(argDsc->GetOtherArgReg()); } else { assert((genRegMask(argDsc->GetOtherArgReg()) & fullIntArgRegMask(compiler->info.compCallConv)) != RBM_NONE); - intRegState->rsCalleeRegArgMaskLiveIn |= genRegMask(argDsc->GetOtherArgReg()); + intRegState->rsCalleeRegArgMaskLiveIn.AddRegNumInMask(argDsc->GetOtherArgReg()); } } } @@ -2388,7 +2393,7 @@ void LinearScan::buildIntervals() // If there is a secret stub param, it is also live in if (compiler->info.compPublishStubParam) { - intRegState->rsCalleeRegArgMaskLiveIn |= RBM_SECRET_STUB_PARAM; + intRegState->rsCalleeRegArgMaskLiveIn.AddGprRegs(RBM_SECRET_STUB_PARAM); LclVarDsc* stubParamDsc = compiler->lvaGetDesc(compiler->lvaStubArgumentVar); if (isCandidateVar(stubParamDsc)) @@ -2543,7 +2548,8 @@ void LinearScan::buildIntervals() killed = RBM_EDI | RBM_ECX | RBM_EAX; #else // Poisoning uses REG_SCRATCH for small vars and memset helper for big vars. - killed = genRegMask(REG_SCRATCH) | compiler->compHelperCallKillSet(CORINFO_HELP_NATIVE_MEMSET); + killed = compiler->compHelperCallKillSet(CORINFO_HELP_NATIVE_MEMSET); + killed.AddRegNumInMask(REG_SCRATCH); #endif addKillForRegs(killed, currentLoc + 1); currentLoc += 2; @@ -2825,14 +2831,29 @@ void LinearScan::buildIntervals() availableRegCount = REG_INT_COUNT; } - if (availableRegCount < (sizeof(regMaskTP) * 8)) +#ifdef HAS_MORE_THAN_64_REGISTERS + static_assert((sizeof(regMaskTP) == 2 * sizeof(regMaskSmall)), "check the size of regMaskTP"); +#else + static_assert((sizeof(regMaskTP) == sizeof(regMaskSmall)), "check the size of regMaskTP"); +#endif + + if (availableRegCount < (sizeof(regMaskSmall) * 8)) + { + // Mask out the bits that are between (8 * regMaskSmall) ~ availableRegCount + actualRegistersMask = regMaskTP((1ULL << availableRegCount) - 1); + } +#ifdef HAS_MORE_THAN_64_REGISTERS + else if (availableRegCount < (sizeof(regMaskTP) * 8)) { - // Mask out the bits that are between 64 ~ availableRegCount - actualRegistersMask = (1ULL << availableRegCount) - 1; + // Mask out the bits that are between (8 * regMaskTP) ~ availableRegCount + // Subtract one extra for stack. + unsigned topRegCount = availableRegCount - sizeof(regMaskSmall) * 8 - 1; + actualRegistersMask = regMaskTP(~RBM_NONE, (1ULL << topRegCount) - 1); } +#endif else { - actualRegistersMask = ~RBM_NONE; + actualRegistersMask = regMaskTP(~RBM_NONE, ~0); } #ifdef DEBUG @@ -2870,9 +2891,9 @@ void LinearScan::buildInitialParamDef(const LclVarDsc* varDsc, regNumber paramRe { // Set this interval as currently assigned to that register assert(paramReg < REG_COUNT); - mask = genRegMask(paramReg); + mask = genSingleTypeRegMask(paramReg); assignPhysReg(paramReg, interval); - INDEBUG(registersToDump |= getRegMask(paramReg, interval->registerType)); + INDEBUG(registersToDump.AddRegNum(paramReg, interval->registerType)); } RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeParamDef, nullptr, mask); pos->setRegOptional(true); @@ -2933,7 +2954,7 @@ void LinearScan::stressSetRandomParameterPreferences() } *regs &= ~genRegMask(prefReg); - interval->mergeRegisterPreferences(genRegMask(prefReg)); + interval->mergeRegisterPreferences(genSingleTypeRegMask(prefReg)); } } @@ -3061,7 +3082,8 @@ RefPosition* LinearScan::BuildDef(GenTree* tree, SingleTypeRegSet dstCandidates, if (dstCandidates != RBM_NONE) { - assert((tree->GetRegNum() == REG_NA) || (dstCandidates == genRegMask(tree->GetRegByIndex(multiRegIdx)))); + assert((tree->GetRegNum() == REG_NA) || + (dstCandidates == genSingleTypeRegMask(tree->GetRegByIndex(multiRegIdx)))); } RegisterType type; @@ -3084,8 +3106,8 @@ RefPosition* LinearScan::BuildDef(GenTree* tree, SingleTypeRegSet dstCandidates, { if (!tree->IsMultiRegNode() || (multiRegIdx == 0)) { - assert((dstCandidates == RBM_NONE) || (dstCandidates == genRegMask(tree->GetRegNum()))); - dstCandidates = genRegMask(tree->GetRegNum()); + assert((dstCandidates == RBM_NONE) || (dstCandidates == genSingleTypeRegMask(tree->GetRegNum()))); + dstCandidates = genSingleTypeRegMask(tree->GetRegNum()); } else { @@ -3166,7 +3188,7 @@ void LinearScan::BuildCallDefs(GenTree* tree, int dstCount, regMaskTP dstCandida assert(dstCandidates.IsRegNumInMask(thisReg)); dstCandidates.RemoveRegNumFromMask(thisReg); - BuildDef(tree, genRegMask(thisReg), i); + BuildDef(tree, genSingleTypeRegMask(thisReg), i); } } @@ -3219,7 +3241,7 @@ void LinearScan::BuildKills(GenTree* tree, regMaskTP killMask) // Call this even when killMask is RBM_NONE, as we have to check for some special cases buildKillPositionsForNode(tree, currentLoc + 1, killMask); - if (killMask != RBM_NONE) + if (killMask.IsNonEmpty()) { #if FEATURE_PARTIAL_SIMD_CALLEE_SAVE // Build RefPositions to account for the fact that, even in a callee-save register, the upper half of any large @@ -3303,7 +3325,7 @@ void LinearScan::BuildDefWithKills(GenTree* tree, int dstCount, SingleTypeRegSet } #endif // TARGET_64BIT } -#endif // defined(TARGET_ARMARCH) || defined(TARGET_RISCV64) +#endif // defined(TARGET_ARMARCH) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) //------------------------------------------------------------------------ // BuildCallDefsWithKills: Build one or more RefTypeDef RefPositions for the given node, @@ -3323,7 +3345,7 @@ void LinearScan::BuildDefWithKills(GenTree* tree, int dstCount, SingleTypeRegSet void LinearScan::BuildCallDefsWithKills(GenTree* tree, int dstCount, regMaskTP dstCandidates, regMaskTP killMask) { assert(dstCount > 0); - assert(dstCandidates != RBM_NONE); + assert(dstCandidates.IsNonEmpty()); // Build the kill RefPositions BuildKills(tree, killMask); @@ -3353,7 +3375,7 @@ void LinearScan::UpdatePreferencesOfDyingLocal(Interval* interval) // _after_ the call, then we are going to prefer callee-saved registers for // such local anyway, so there is no need to look at such local uses. // - if (placedArgRegs == RBM_NONE) + if (placedArgRegs.IsEmpty()) { return; } @@ -4225,7 +4247,7 @@ int LinearScan::BuildReturn(GenTree* tree) { hasMismatchedRegTypes = true; SingleTypeRegSet dstRegMask = - genRegMask(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv)); + genSingleTypeRegMask(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv)); if (varTypeUsesIntReg(dstType)) { @@ -4252,7 +4274,9 @@ int LinearScan::BuildReturn(GenTree* tree) if (!hasMismatchedRegTypes || (regType(op1->AsLclVar()->GetFieldTypeByIndex(compiler, i)) == regType(retTypeDesc.GetReturnRegType(i)))) { - BuildUse(op1, genRegMask(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv)), i); + BuildUse(op1, + genSingleTypeRegMask(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv)), + i); } else { @@ -4280,7 +4304,11 @@ int LinearScan::BuildReturn(GenTree* tree) break; case TYP_DOUBLE: // We ONLY want the valid double register in the RBM_DOUBLERET mask. - useCandidates = (RBM_DOUBLERET & RBM_ALLDOUBLE); +#ifdef TARGET_AMD64 + useCandidates = (RBM_DOUBLERET & RBM_ALLDOUBLE).GetFloatRegSet(); +#else + useCandidates = (RBM_DOUBLERET & RBM_ALLDOUBLE); +#endif // TARGET_AMD64 break; case TYP_LONG: useCandidates = RBM_LNGRET; @@ -4379,11 +4407,11 @@ int LinearScan::BuildPutArgReg(GenTreeUnOp* node) // To avoid redundant moves, have the argument operand computed in the // register in which the argument is passed to the call. - SingleTypeRegSet argMask = genRegMask(argReg); + SingleTypeRegSet argMask = genSingleTypeRegMask(argReg); RefPosition* use = BuildUse(op1, argMask); // Record that this register is occupied by a register now. - placedArgRegs |= argMask; + placedArgRegs.AddRegNumInMask(argReg); if (supportsSpecialPutArg() && isCandidateLocalRef(op1) && ((op1->gtFlags & GTF_VAR_DEATH) == 0)) { @@ -4411,7 +4439,7 @@ int LinearScan::BuildPutArgReg(GenTreeUnOp* node) if (node->TypeGet() == TYP_LONG) { srcCount++; - SingleTypeRegSet argMaskHi = genRegMask(REG_NEXT(argReg)); + SingleTypeRegSet argMaskHi = genSingleTypeRegMask(REG_NEXT(argReg)); assert(genRegArgNext(argReg) == REG_NEXT(argReg)); use = BuildUse(op1, argMaskHi, 1); BuildDef(node, argMask, 0); @@ -4457,7 +4485,7 @@ void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* c regNumber argReg = argNode->GetRegNum(); regNumber targetReg = compiler->getCallArgIntRegister(argReg); - buildInternalIntRegisterDefForNode(call, genRegMask(targetReg)); + buildInternalIntRegisterDefForNode(call, genSingleTypeRegMask(targetReg)); } } diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp index c77b8d5c557d3..dd2805be4bcbe 100644 --- a/src/coreclr/jit/lsraloongarch64.cpp +++ b/src/coreclr/jit/lsraloongarch64.cpp @@ -681,7 +681,7 @@ int LinearScan::BuildCall(GenTreeCall* call) { bool hasMultiRegRetVal = false; const ReturnTypeDesc* retTypeDesc = nullptr; - regMaskTP singleDstCandidates = RBM_NONE; + SingleTypeRegSet singleDstCandidates = RBM_NONE; int srcCount = 0; int dstCount = 0; @@ -700,8 +700,8 @@ int LinearScan::BuildCall(GenTreeCall* call) } } - GenTree* ctrlExpr = call->gtControlExpr; - regMaskTP ctrlExprCandidates = RBM_NONE; + GenTree* ctrlExpr = call->gtControlExpr; + SingleTypeRegSet ctrlExprCandidates = RBM_NONE; if (call->gtCallType == CT_INDIRECT) { // either gtControlExpr != null or gtCallAddr != null. @@ -731,7 +731,7 @@ int LinearScan::BuildCall(GenTreeCall* call) { // For R2R and VSD we have stub address in REG_R2R_INDIRECT_PARAM // and will load call address into the temp register from this register. - regMaskTP candidates = RBM_NONE; + SingleTypeRegSet candidates = RBM_NONE; if (call->IsFastTailCall()) { candidates = (allRegs(TYP_INT) & (RBM_INT_CALLEE_TRASH & ~RBM_GSCOOKIE_TMP)); @@ -794,7 +794,7 @@ int LinearScan::BuildCall(GenTreeCall* call) #ifdef DEBUG assert(use.GetNode()->OperIs(GT_PUTARG_REG)); #endif - BuildUse(use.GetNode(), genRegMask(use.GetNode()->GetRegNum())); + BuildUse(use.GetNode(), genSingleTypeRegMask(use.GetNode()->GetRegNum())); srcCount++; } } @@ -804,7 +804,7 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(regCount == abiInfo.NumRegs); for (unsigned int i = 0; i < regCount; i++) { - BuildUse(argNode, genRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); + BuildUse(argNode, genSingleTypeRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); } srcCount += regCount; } @@ -814,7 +814,7 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(argNode->GetRegNum() == argReg); HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs); { - BuildUse(argNode, genRegMask(argNode->GetRegNum())); + BuildUse(argNode, genSingleTypeRegMask(argNode->GetRegNum())); srcCount++; } } @@ -994,14 +994,16 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) // Registers for split argument corresponds to source int dstCount = argNode->gtNumRegs; - regNumber argReg = argNode->GetRegNum(); - regMaskTP argMask = RBM_NONE; + regNumber argReg = argNode->GetRegNum(); + SingleTypeRegSet argMask = RBM_NONE; for (unsigned i = 0; i < argNode->gtNumRegs; i++) { regNumber thisArgReg = (regNumber)((unsigned)argReg + i); - argMask |= genRegMask(thisArgReg); + argMask |= genSingleTypeRegMask(thisArgReg); argNode->SetRegNumByIdx(thisArgReg, i); } + assert((argMask == RBM_NONE) || ((argMask & availableIntRegs) != RBM_NONE) || + ((argMask & availableFloatRegs) != RBM_NONE)); if (putArgChild->OperGet() == GT_FIELD_LIST) { @@ -1023,10 +1025,10 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) // Consume all the registers, setting the appropriate register mask for the ones that // go into registers. - regMaskTP sourceMask = RBM_NONE; + SingleTypeRegSet sourceMask = RBM_NONE; if (sourceRegCount < argNode->gtNumRegs) { - sourceMask = genRegMask((regNumber)((unsigned)argReg + sourceRegCount)); + sourceMask = genSingleTypeRegMask((regNumber)((unsigned)argReg + sourceRegCount)); } sourceRegCount++; BuildUse(node, sourceMask, 0); @@ -1138,7 +1140,7 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) // We don't need to materialize the struct size but we still need // a temporary register to perform the sequence of loads and stores. // We can't use the special Write Barrier registers, so exclude them from the mask - regMaskTP internalIntCandidates = + SingleTypeRegSet internalIntCandidates = allRegs(TYP_INT) & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF); buildInternalIntRegisterDefForNode(blkNode, internalIntCandidates); diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index ebea9cce71472..2983c035fe8d1 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -885,7 +885,8 @@ int LinearScan::BuildCall(GenTreeCall* call) ctrlExprCandidates = allRegs(TYP_INT) & RBM_INT_CALLEE_TRASH; if (compiler->getNeedsGSSecurityCookie()) { - ctrlExprCandidates &= ~(genRegMask(REG_GSCOOKIE_TMP_0) | genRegMask(REG_GSCOOKIE_TMP_1)); + ctrlExprCandidates &= + ~(genSingleTypeRegMask(REG_GSCOOKIE_TMP_0) | genSingleTypeRegMask(REG_GSCOOKIE_TMP_1)); } assert(ctrlExprCandidates != RBM_NONE); } @@ -957,7 +958,7 @@ int LinearScan::BuildCall(GenTreeCall* call) #ifdef DEBUG assert(use.GetNode()->OperIs(GT_PUTARG_REG)); #endif - BuildUse(use.GetNode(), genRegMask(use.GetNode()->GetRegNum())); + BuildUse(use.GetNode(), genSingleTypeRegMask(use.GetNode()->GetRegNum())); srcCount++; } } @@ -967,7 +968,7 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(regCount == abiInfo.NumRegs); for (unsigned int i = 0; i < regCount; i++) { - BuildUse(argNode, genRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); + BuildUse(argNode, genSingleTypeRegMask(argNode->AsPutArgSplit()->GetRegNumByIdx(i)), i); } srcCount += regCount; } @@ -977,7 +978,7 @@ int LinearScan::BuildCall(GenTreeCall* call) assert(argNode->GetRegNum() == argReg); HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs); { - BuildUse(argNode, genRegMask(argNode->GetRegNum())); + BuildUse(argNode, genSingleTypeRegMask(argNode->GetRegNum())); srcCount++; } } @@ -1149,7 +1150,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) for (unsigned i = 0; i < argNode->gtNumRegs; i++) { regNumber thisArgReg = (regNumber)((unsigned)argReg + i); - argMask |= genRegMask(thisArgReg); + argMask |= genSingleTypeRegMask(thisArgReg); argNode->SetRegNumByIdx(thisArgReg, i); } assert((argMask == RBM_NONE) || ((argMask & availableIntRegs) != RBM_NONE) || @@ -1181,7 +1182,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) SingleTypeRegSet sourceMask = RBM_NONE; if (sourceRegCount < argNode->gtNumRegs) { - sourceMask = genRegMask((regNumber)((unsigned)argReg + sourceRegCount)); + sourceMask = genSingleTypeRegMask((regNumber)((unsigned)argReg + sourceRegCount)); } sourceRegCount++; BuildUse(node, sourceMask, regIndex); diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index fe21be0ec8938..d3a7f075fdd08 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -1259,7 +1259,7 @@ int LinearScan::BuildCall(GenTreeCall* call) if (argNode->OperIsPutArgReg()) { srcCount++; - BuildUse(argNode, genRegMask(argNode->GetRegNum())); + BuildUse(argNode, genSingleTypeRegMask(argNode->GetRegNum())); } #ifdef UNIX_AMD64_ABI else if (argNode->OperGet() == GT_FIELD_LIST) @@ -1268,7 +1268,7 @@ int LinearScan::BuildCall(GenTreeCall* call) { assert(use.GetNode()->OperIsPutArgReg()); srcCount++; - BuildUse(use.GetNode(), genRegMask(use.GetNode()->GetRegNum())); + BuildUse(use.GetNode(), genSingleTypeRegMask(use.GetNode()->GetRegNum())); } } #endif // UNIX_AMD64_ABI diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b277605050ac0..89fae6f436709 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -9426,7 +9426,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA } /* Any constant cases should have been folded earlier */ - noway_assert(!op1->OperIsConst() || opts.OptimizationDisabled() || optValnumCSE_phase); + noway_assert(!op1->OperIsConst() || op1->IsIconHandle() || opts.OptimizationDisabled() || + optValnumCSE_phase); break; case GT_CKFINITE: @@ -10709,7 +10710,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) GenTree* op1 = node->Op(1); GenTree* op2 = node->Op(2); - if (!op1->OperIsHWIntrinsic(NI_Sve_CreateTrueMaskAll) && + if (!op1->OperIsHWIntrinsic(NI_Sve_CreateTrueMaskAll) || !op2->OperIsHWIntrinsic(NI_Sve_ConvertMaskToVector)) { break; diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index de20de7bfbe5b..d778414428272 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -109,6 +109,7 @@ enum NamedIntrinsic : unsigned short NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan, NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant, + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences, NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference, @@ -140,6 +141,10 @@ enum NamedIntrinsic : unsigned short NI_System_Threading_Interlocked_MemoryBarrier, NI_System_Threading_Interlocked_ReadMemoryBarrier, + // These two are special marker IDs so that we still get the inlining profitability boost + NI_System_Numerics_Intrinsic, + NI_System_Runtime_Intrinsics_Intrinsic, + #ifdef FEATURE_HW_INTRINSICS NI_HW_INTRINSIC_START, #if defined(TARGET_XARCH) diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 5c7b97b306bf4..1b028eca78b84 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -5916,17 +5916,8 @@ void Compiler::optRemoveRedundantZeroInits() Statement* next = stmt->GetNextStmt(); for (GenTree* const tree : stmt->TreeList()) { - if (((tree->gtFlags & GTF_CALL) != 0)) - { - // if this is not a No-GC helper - if (!tree->IsCall() || !emitter::emitNoGChelper(tree->AsCall()->GetHelperNum())) - { - // assume that we have a safe point. - hasGCSafePoint = true; - } - } - hasImplicitControlFlow |= hasEHSuccs && ((tree->gtFlags & GTF_EXCEPT) != 0); + hasGCSafePoint |= IsPotentialGCSafePoint(tree); switch (tree->gtOper) { diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index d9b69b8df5aa2..f2da4a40e755f 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -9,24 +9,27 @@ // RewriteNodeAsCall : Replace the given tree node by a GT_CALL. // // Arguments: -// ppTree - A pointer-to-a-pointer for the tree node -// fgWalkData - A pointer to tree walk data providing the context -// callHnd - The method handle of the call to be generated -// entryPoint - The method entrypoint of the call to be generated -// args - The argument list of the call to be generated +// use - A pointer-to-a-pointer for the tree node +// sig - The signature info for callHnd +// parents - A pointer to tree walk data providing the context +// callHnd - The method handle of the call to be generated +// entryPoint - The method entrypoint of the call to be generated +// operands - The operand list of the call to be generated +// operandCount - The number of operands in the operand list // // Return Value: // None. // void Rationalizer::RewriteNodeAsCall(GenTree** use, + CORINFO_SIG_INFO* sig, ArrayStack& parents, CORINFO_METHOD_HANDLE callHnd, -#ifdef FEATURE_READYTORUN +#if defined(FEATURE_READYTORUN) CORINFO_CONST_LOOKUP entryPoint, -#endif - GenTree* arg1, - GenTree* arg2) +#endif // FEATURE_READYTORUN + GenTree** operands, + size_t operandCount) { GenTree* const tree = *use; GenTree* const treeFirstNode = comp->fgGetFirstNode(tree); @@ -37,57 +40,154 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, // Create the call node GenTreeCall* call = comp->gtNewCallNode(CT_USER_FUNC, callHnd, tree->gtType); - if (arg2 != nullptr) + assert(sig != nullptr); + var_types retType = JITtype2varType(sig->retType); + + if (varTypeIsStruct(retType)) { - call->gtArgs.PushFront(comp, NewCallArg::Primitive(arg2)); - call->gtFlags |= arg2->gtFlags & GTF_ALL_EFFECT; + call->gtRetClsHnd = sig->retTypeClass; + retType = comp->impNormStructType(sig->retTypeClass); + + if (retType != call->gtType) + { + assert(varTypeIsSIMD(retType)); + call->ChangeType(retType); + } + +#if FEATURE_MULTIREG_RET + call->InitializeStructReturnType(comp, sig->retTypeClass, call->GetUnmanagedCallConv()); +#endif // FEATURE_MULTIREG_RET + + Compiler::structPassingKind howToReturnStruct; + var_types returnType = + comp->getReturnTypeForStruct(sig->retTypeClass, call->GetUnmanagedCallConv(), &howToReturnStruct); + + if (howToReturnStruct == Compiler::SPK_ByReference) + { + assert(returnType == TYP_UNKNOWN); + call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG; + } } - if (arg1 != nullptr) + CORINFO_ARG_LIST_HANDLE sigArg = sig->args; + size_t firstArg = 0; + + if (sig->hasThis()) { - call->gtArgs.PushFront(comp, NewCallArg::Primitive(arg1)); - call->gtFlags |= arg1->gtFlags & GTF_ALL_EFFECT; + GenTree* operand = operands[0]; + NewCallArg arg = NewCallArg::Primitive(operand).WellKnown(WellKnownArg::ThisPointer); + + call->gtArgs.PushBack(comp, arg); + call->gtFlags |= operand->gtFlags & GTF_ALL_EFFECT; + + firstArg++; } -#if DEBUG - CORINFO_SIG_INFO sig; - comp->eeGetMethodSig(callHnd, &sig); - assert(JITtype2varType(sig.retType) == tree->gtType); -#endif // DEBUG + for (size_t i = firstArg; i < operandCount; i++) + { + GenTree* operand = operands[i]; + + CORINFO_CLASS_HANDLE clsHnd = NO_CLASS_HANDLE; + CorInfoType corTyp = strip(comp->info.compCompHnd->getArgType(sig, sigArg, &clsHnd)); + var_types sigTyp = JITtype2varType(corTyp); + + NewCallArg arg; + + if (varTypeIsStruct(sigTyp)) + { + // GenTreeFieldList should not have been introduced + // for intrinsics that get rewritten back to user calls + assert(!operand->OperIsFieldList()); + + sigTyp = comp->impNormStructType(clsHnd); + arg = NewCallArg::Struct(operand, sigTyp, clsHnd); + } + else + { + arg = NewCallArg::Primitive(operand, sigTyp); + } + + call->gtArgs.PushBack(comp, arg); + call->gtFlags |= operand->gtFlags & GTF_ALL_EFFECT; -#ifdef FEATURE_READYTORUN + sigArg = comp->info.compCompHnd->getArgNext(sigArg); + } + +#if defined(FEATURE_READYTORUN) call->AsCall()->setEntryPoint(entryPoint); -#endif +#endif // FEATURE_READYTORUN + + unsigned tmpNum = BAD_VAR_NUM; + + if (call->TreatAsShouldHaveRetBufArg()) + { + assert(call->ShouldHaveRetBufArg()); + + tmpNum = comp->lvaGrabTemp(true DEBUGARG("return buffer for hwintrinsic")); + comp->lvaSetStruct(tmpNum, sig->retTypeClass, false); + + GenTree* destAddr = comp->gtNewLclVarAddrNode(tmpNum, TYP_BYREF); + NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer); + + call->gtArgs.InsertAfterThisOrFirst(comp, newArg); + call->gtType = TYP_VOID; + } call = comp->fgMorphArgs(call); + GenTree* result = call; + // Replace "tree" with "call" if (parents.Height() > 1) { - parents.Top(1)->ReplaceOperand(use, call); + if (tmpNum != BAD_VAR_NUM) + { + result = comp->gtNewLclvNode(tmpNum, retType); + } + + parents.Top(1)->ReplaceOperand(use, result); + + if (tmpNum != BAD_VAR_NUM) + { + comp->gtSetEvalOrder(result); + BlockRange().InsertAfter(insertionPoint, LIR::Range(comp->fgSetTreeSeq(result), result)); + } } else { // If there's no parent, the tree being replaced is the root of the // statement (and no special handling is necessary). - *use = call; + *use = result; } comp->gtSetEvalOrder(call); BlockRange().InsertAfter(insertionPoint, LIR::Range(comp->fgSetTreeSeq(call), call)); - // Propagate flags of "call" to its parents. - // 0 is current node, so start at 1 - for (int i = 1; i < parents.Height(); i++) + if (result == call) + { + // Propagate flags of "call" to its parents. + // 0 is current node, so start at 1 + for (int i = 1; i < parents.Height(); i++) + { + parents.Top(i)->gtFlags |= (call->gtFlags & GTF_ALL_EFFECT) | GTF_CALL; + } + } + else { - parents.Top(i)->gtFlags |= (call->gtFlags & GTF_ALL_EFFECT) | GTF_CALL; + // Normally the call replaces the node in pre-order, so we automatically continue visiting the call. + // However, when we have a retbuf the node is replaced by a local with the call inserted before it, + // so we need to make sure we visit it here. + RationalizeVisitor visitor(*this); + GenTree* node = call; + visitor.WalkTree(&node, nullptr); + assert(node == call); } - // Since "tree" is replaced with "call", pop "tree" node (i.e the current node) - // and replace it with "call" on parent stack. + // Since "tree" is replaced with "result", pop "tree" node (i.e the current node) + // and replace it with "result" on parent stack. assert(parents.Top() == tree); (void)parents.Pop(); - parents.Push(call); + parents.Push(result); } // RewriteIntrinsicAsUserCall : Rewrite an intrinsic operator as a GT_CALL to the original method. @@ -108,14 +208,274 @@ void Rationalizer::RewriteIntrinsicAsUserCall(GenTree** use, ArrayStackAsIntrinsic(); - GenTree* arg1 = intrinsic->gtGetOp1(); - GenTree* arg2 = intrinsic->gtGetOp2(); - RewriteNodeAsCall(use, parents, intrinsic->gtMethodHandle, -#ifdef FEATURE_READYTORUN + GenTree* operands[2]; + size_t operandCount = 0; + + operands[0] = intrinsic->gtGetOp1(); + + if (operands[0] != nullptr) + { + operandCount++; + } + + operands[1] = intrinsic->gtGetOp2(); + + if (operands[1] != nullptr) + { + operandCount++; + } + + CORINFO_METHOD_HANDLE callHnd = intrinsic->gtMethodHandle; + + CORINFO_SIG_INFO sigInfo; + comp->eeGetMethodSig(callHnd, &sigInfo); + + RewriteNodeAsCall(use, &sigInfo, parents, callHnd, +#if defined(FEATURE_READYTORUN) intrinsic->gtEntryPoint, +#endif // FEATURE_READYTORUN + operands, operandCount); +} + +#if defined(FEATURE_HW_INTRINSICS) +// RewriteHWIntrinsicAsUserCall : Rewrite a hwintrinsic node as a GT_CALL to the original method. +// +// Arguments: +// ppTree - A pointer-to-a-pointer for the intrinsic node +// fgWalkData - A pointer to tree walk data providing the context +// +// Return Value: +// None. +void Rationalizer::RewriteHWIntrinsicAsUserCall(GenTree** use, ArrayStack& parents) +{ + GenTreeHWIntrinsic* hwintrinsic = (*use)->AsHWIntrinsic(); + NamedIntrinsic intrinsicId = hwintrinsic->GetHWIntrinsicId(); + CorInfoType simdBaseJitType = hwintrinsic->GetSimdBaseJitType(); + var_types simdBaseType = hwintrinsic->GetSimdBaseType(); + uint32_t simdSize = hwintrinsic->GetSimdSize(); + var_types retType = hwintrinsic->TypeGet(); + + GenTree** operands = hwintrinsic->GetOperandArray(); + size_t operandCount = hwintrinsic->GetOperandCount(); + + CORINFO_METHOD_HANDLE callHnd = hwintrinsic->GetMethodHandle(); + + CORINFO_SIG_INFO sigInfo; + comp->eeGetMethodSig(callHnd, &sigInfo); + + GenTree* result = nullptr; + + switch (intrinsicId) + { + case NI_Vector128_Shuffle: +#if defined(TARGET_XARCH) + case NI_Vector256_Shuffle: + case NI_Vector512_Shuffle: +#elif defined(TARGET_ARM64) + case NI_Vector64_Shuffle: +#endif + { + assert(operandCount == 2); +#if defined(TARGET_XARCH) + assert((simdSize == 16) || (simdSize == 32) || (simdSize == 64)); +#else + assert((simdSize == 8) || (simdSize == 16)); #endif - arg1, arg2); + + GenTree* op1 = operands[0]; + GenTree* op2 = operands[1]; + + if (op2->IsVectorConst() && comp->IsValidForShuffle(op2->AsVecCon(), simdSize, simdBaseType)) + { + result = comp->gtNewSimdShuffleNode(retType, op1, op2, simdBaseJitType, simdSize); + } + break; + } + + case NI_Vector128_WithElement: +#if defined(TARGET_XARCH) + case NI_Vector256_WithElement: + case NI_Vector512_WithElement: +#elif defined(TARGET_ARM64) + case NI_Vector64_WithElement: +#endif + { + assert(operandCount == 3); + + GenTree* op1 = operands[0]; + GenTree* op2 = operands[1]; + GenTree* op3 = operands[2]; + + if (op2->OperIsConst()) + { + ssize_t imm8 = op2->AsIntCon()->IconValue(); + ssize_t count = simdSize / genTypeSize(simdBaseType); + + if ((imm8 >= count) || (imm8 < 0)) + { + // Using software fallback if index is out of range (throw exception) + break; + } + + result = comp->gtNewSimdWithElementNode(retType, op1, op2, op3, simdBaseJitType, simdSize); + break; + } + break; + } + + default: + { + if (sigInfo.numArgs == 0) + { + break; + } + + GenTree* immOp1 = nullptr; + GenTree* immOp2 = nullptr; + + int immLowerBound = 0; + int immUpperBound = 0; + bool hasFullRangeImm = false; + bool mustExpand = false; + bool useFallback = false; + +#if defined(TARGET_XARCH) + immOp1 = operands[operandCount - 1]; + + if (!HWIntrinsicInfo::isImmOp(intrinsicId, immOp1)) + { + break; + } + + immUpperBound = HWIntrinsicInfo::lookupImmUpperBound(intrinsicId); + hasFullRangeImm = HWIntrinsicInfo::HasFullRangeImm(intrinsicId); +#elif defined(TARGET_ARM64) + if (!HWIntrinsicInfo::HasImmediateOperand(intrinsicId)) + { + break; + } + + CORINFO_CLASS_HANDLE op1ClsHnd = NO_CLASS_HANDLE; + CORINFO_CLASS_HANDLE op2ClsHnd = NO_CLASS_HANDLE; + CORINFO_CLASS_HANDLE op3ClsHnd = NO_CLASS_HANDLE; + + size_t argCount = operandCount - (sigInfo.hasThis() ? 1 : 0); + + if (argCount > 0) + { + CORINFO_ARG_LIST_HANDLE args = sigInfo.args; + comp->info.compCompHnd->getArgType(&sigInfo, args, &op1ClsHnd); + + if (argCount > 1) + { + args = comp->info.compCompHnd->getArgNext(args); + comp->info.compCompHnd->getArgType(&sigInfo, args, &op2ClsHnd); + + if (argCount > 2) + { + args = comp->info.compCompHnd->getArgNext(args); + comp->info.compCompHnd->getArgType(&sigInfo, args, &op3ClsHnd); + } + } + } + + // Position of the immediates from top of stack + int imm1Pos = -1; + int imm2Pos = -1; + + HWIntrinsicInfo::GetImmOpsPositions(intrinsicId, &sigInfo, &imm1Pos, &imm2Pos); + + if (imm1Pos >= 0) + { + immOp1 = operands[operandCount - (1 + imm1Pos)]; + assert(HWIntrinsicInfo::isImmOp(intrinsicId, immOp1)); + } + + if (imm2Pos >= 0) + { + immOp2 = operands[operandCount - (1 + imm2Pos)]; + assert(HWIntrinsicInfo::isImmOp(intrinsicId, immOp2)); + } + + unsigned immSimdSize = simdSize; + var_types immSimdBaseType = simdBaseType; + + if (immOp2 != nullptr) + { + comp->getHWIntrinsicImmTypes(intrinsicId, &sigInfo, 2, simdBaseType, simdBaseJitType, op2ClsHnd, + op3ClsHnd, &immSimdSize, &immSimdBaseType); + HWIntrinsicInfo::lookupImmBounds(intrinsicId, immSimdSize, immSimdBaseType, 2, &immLowerBound, + &immUpperBound); + + if (comp->CheckHWIntrinsicImmRange(intrinsicId, simdBaseJitType, immOp2, mustExpand, immLowerBound, + immUpperBound, hasFullRangeImm, &useFallback)) + { + // Set this as nullptr so we stay an intrinsic if both immediates are constant and in range + immOp2 = nullptr; + } + + immSimdSize = simdSize; + immSimdBaseType = simdBaseType; + } + + comp->getHWIntrinsicImmTypes(intrinsicId, &sigInfo, 1, simdBaseType, simdBaseJitType, op2ClsHnd, op3ClsHnd, + &immSimdSize, &immSimdBaseType); + HWIntrinsicInfo::lookupImmBounds(intrinsicId, immSimdSize, immSimdBaseType, 1, &immLowerBound, + &immUpperBound); +#endif + + if ((immOp2 == nullptr) && (immOp1 != nullptr)) + { + if (comp->CheckHWIntrinsicImmRange(intrinsicId, simdBaseJitType, immOp1, mustExpand, immLowerBound, + immUpperBound, hasFullRangeImm, &useFallback)) + { + // We're already in the right shape, so just stop tracking ourselves as a user call + hwintrinsic->gtFlags &= ~GTF_HW_USER_CALL; + return; + } + } + break; + } + } + + if (result != nullptr) + { + GenTree* const hwintrinsicFirstNode = comp->fgGetFirstNode(hwintrinsic); + GenTree* const insertionPoint = hwintrinsicFirstNode->gtPrev; + + BlockRange().Remove(hwintrinsicFirstNode, hwintrinsic); + + // Replace "tree" with "call" + if (parents.Height() > 1) + { + parents.Top(1)->ReplaceOperand(use, result); + } + else + { + // If there's no parent, the tree being replaced is the root of the + // statement (and no special handling is necessary). + *use = result; + } + + comp->gtSetEvalOrder(result); + BlockRange().InsertAfter(insertionPoint, LIR::Range(comp->fgSetTreeSeq(result), result)); + + // Since "hwintrinsic" is replaced with "result", pop "hwintrinsic" node (i.e the current node) + // and replace it with "result" on parent stack. + assert(parents.Top() == hwintrinsic); + (void)parents.Pop(); + parents.Push(result); + + return; + } + + RewriteNodeAsCall(use, &sigInfo, parents, callHnd, +#if defined(FEATURE_READYTORUN) + hwintrinsic->GetEntryPoint(), +#endif // FEATURE_READYTORUN + operands, operandCount); } +#endif // FEATURE_HW_INTRINSICS #ifdef TARGET_ARM64 // RewriteSubLshDiv: Possibly rewrite a SubLshDiv node into a Mod. @@ -319,6 +679,13 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge assert(comp->IsTargetIntrinsic(node->AsIntrinsic()->gtIntrinsicName)); break; +#if defined(FEATURE_HW_INTRINSICS) + case GT_HWINTRINSIC: + // Intrinsics should have already been rewritten back into user calls. + assert(!node->AsHWIntrinsic()->IsUserCall()); + break; +#endif // FEATURE_HW_INTRINSICS + case GT_CAST: if (node->AsCast()->CastOp()->OperIsSimple()) { @@ -361,63 +728,55 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge return Compiler::WALK_CONTINUE; } -//------------------------------------------------------------------------ -// DoPhase: Run the rationalize over the method IR. -// -// Returns: -// PhaseStatus indicating, what, if anything, was modified -// -PhaseStatus Rationalizer::DoPhase() +// Rewrite intrinsics that are not supported by the target back into user calls. +// This needs to be done before the transition to LIR because it relies on the use +// of fgMorphArgs, which is designed to operate on HIR. Once this is done for a +// particular statement, link that statement's nodes into the current basic block. +Compiler::fgWalkResult Rationalizer::RationalizeVisitor::PreOrderVisit(GenTree** use, GenTree* user) { - class RationalizeVisitor final : public GenTreeVisitor - { - Rationalizer& m_rationalizer; + GenTree* const node = *use; - public: - enum - { - ComputeStack = true, - DoPreOrder = true, - DoPostOrder = true, - UseExecutionOrder = true, - }; - - RationalizeVisitor(Rationalizer& rationalizer) - : GenTreeVisitor(rationalizer.comp) - , m_rationalizer(rationalizer) + if (node->OperGet() == GT_INTRINSIC) + { + if (m_rationalizer.comp->IsIntrinsicImplementedByUserCall(node->AsIntrinsic()->gtIntrinsicName)) { + m_rationalizer.RewriteIntrinsicAsUserCall(use, this->m_ancestors); } - - // Rewrite intrinsics that are not supported by the target back into user calls. - // This needs to be done before the transition to LIR because it relies on the use - // of fgMorphArgs, which is designed to operate on HIR. Once this is done for a - // particular statement, link that statement's nodes into the current basic block. - fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) + } +#if defined(FEATURE_HW_INTRINSICS) + else if (node->OperIsHWIntrinsic()) + { + if (node->AsHWIntrinsic()->IsUserCall()) { - GenTree* const node = *use; - if (node->OperGet() == GT_INTRINSIC && - m_rationalizer.comp->IsIntrinsicImplementedByUserCall(node->AsIntrinsic()->gtIntrinsicName)) - { - m_rationalizer.RewriteIntrinsicAsUserCall(use, this->m_ancestors); - } + m_rationalizer.RewriteHWIntrinsicAsUserCall(use, this->m_ancestors); + } + } +#endif // FEATURE_HW_INTRINSICS #ifdef TARGET_ARM64 - if (node->OperIs(GT_SUB)) - { - m_rationalizer.RewriteSubLshDiv(use); - } + if (node->OperIs(GT_SUB)) + { + m_rationalizer.RewriteSubLshDiv(use); + } #endif - return Compiler::WALK_CONTINUE; - } + return Compiler::WALK_CONTINUE; +} - // Rewrite HIR nodes into LIR nodes. - fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) - { - return m_rationalizer.RewriteNode(use, this->m_ancestors); - } - }; +// Rewrite HIR nodes into LIR nodes. +Compiler::fgWalkResult Rationalizer::RationalizeVisitor::PostOrderVisit(GenTree** use, GenTree* user) +{ + return m_rationalizer.RewriteNode(use, this->m_ancestors); +} +//------------------------------------------------------------------------ +// DoPhase: Run the rationalize over the method IR. +// +// Returns: +// PhaseStatus indicating, what, if anything, was modified +// +PhaseStatus Rationalizer::DoPhase() +{ DBEXEC(TRUE, SanityCheck()); comp->compCurBB = nullptr; diff --git a/src/coreclr/jit/rationalize.h b/src/coreclr/jit/rationalize.h index a8651b2e5b8c7..2d578583abe3f 100644 --- a/src/coreclr/jit/rationalize.h +++ b/src/coreclr/jit/rationalize.h @@ -37,15 +37,19 @@ class Rationalizer final : public Phase // Intrinsic related transformations void RewriteNodeAsCall(GenTree** use, + CORINFO_SIG_INFO* sig, ArrayStack& parents, CORINFO_METHOD_HANDLE callHnd, -#ifdef FEATURE_READYTORUN +#if defined(FEATURE_READYTORUN) CORINFO_CONST_LOOKUP entryPoint, -#endif - GenTree* arg1 = nullptr, - GenTree* arg2 = nullptr); +#endif // FEATURE_READYTORUN + GenTree** operands, + size_t operandCount); void RewriteIntrinsicAsUserCall(GenTree** use, Compiler::GenTreeStack& parents); +#if defined(FEATURE_HW_INTRINSICS) + void RewriteHWIntrinsicAsUserCall(GenTree** use, Compiler::GenTreeStack& parents); +#endif // FEATURE_HW_INTRINSICS #ifdef TARGET_ARM64 void RewriteSubLshDiv(GenTree** use); @@ -53,6 +57,30 @@ class Rationalizer final : public Phase // Root visitor Compiler::fgWalkResult RewriteNode(GenTree** useEdge, Compiler::GenTreeStack& parents); + +private: + class RationalizeVisitor final : public GenTreeVisitor + { + Rationalizer& m_rationalizer; + + public: + enum + { + ComputeStack = true, + DoPreOrder = true, + DoPostOrder = true, + UseExecutionOrder = true, + }; + + RationalizeVisitor(Rationalizer& rationalizer) + : GenTreeVisitor(rationalizer.comp) + , m_rationalizer(rationalizer) + { + } + + fgWalkResult PreOrderVisit(GenTree** use, GenTree* user); + fgWalkResult PostOrderVisit(GenTree** use, GenTree* user); + }; }; inline Rationalizer::Rationalizer(Compiler* _comp) diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index ce5a2733f0534..99e3fbba31ce0 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -117,30 +117,6 @@ static const ValueNumStore::VN_RELATION_KIND s_vnRelations[] = {ValueNumStore::V ValueNumStore::VN_RELATION_KIND::VRK_Swap, ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse}; -//------------------------------------------------------------------------ -// RelopImplicationInfo -// -// Describes information needed to check for and describe the -// inferences between two relops. -// -struct RelopImplicationInfo -{ - // Dominating relop, whose value may be determined by control flow - ValueNum domCmpNormVN = ValueNumStore::NoVN; - // Dominated relop, whose value we would like to determine - ValueNum treeNormVN = ValueNumStore::NoVN; - // Relationship between the two relops, if any - ValueNumStore::VN_RELATION_KIND vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same; - // Can we draw an inference? - bool canInfer = false; - // If canInfer and dominating relop is true, can we infer value of dominated relop? - bool canInferFromTrue = true; - // If canInfer and dominating relop is false, can we infer value of dominated relop? - bool canInferFromFalse = true; - // Reverse the sense of the inference - bool reverseSense = false; -}; - //------------------------------------------------------------------------ // RelopImplicationRule // diff --git a/src/coreclr/jit/regMaskTPOps.cpp b/src/coreclr/jit/regMaskTPOps.cpp index 86de50a08cb95..f315dc81b8455 100644 --- a/src/coreclr/jit/regMaskTPOps.cpp +++ b/src/coreclr/jit/regMaskTPOps.cpp @@ -5,15 +5,130 @@ struct regMaskTP; +// ---------------------------------------------------------- +// AddRegNumForType: Adds `reg` to the mask. +// +void regMaskTP::AddRegNumInMask(regNumber reg) +{ + SingleTypeRegSet value = genSingleTypeRegMask(reg); +#ifdef HAS_MORE_THAN_64_REGISTERS + if (reg < 64) + { + low |= value; + } + else + { + high |= value; + } +#else + low |= value; +#endif +} + +#ifdef TARGET_ARM +// ---------------------------------------------------------- +// AddRegNumForType: Adds `reg` to the mask. It is same as AddRegNumInMask(reg) except +// that it takes `type` as an argument and adds `reg` to the mask for that type. +// +void regMaskTP::AddRegNumInMask(regNumber reg, var_types type) +{ + low |= genSingleTypeRegMask(reg, type); +} + +// ---------------------------------------------------------- +// RemoveRegNumFromMask: Removes `reg` from the mask. It is same as RemoveRegNumFromMask(reg) except +// that it takes `type` as an argument and adds `reg` to the mask for that type. +// +void regMaskTP::RemoveRegNumFromMask(regNumber reg, var_types type) +{ + low &= ~genSingleTypeRegMask(reg, type); +} + +// ---------------------------------------------------------- +// IsRegNumInMask: Removes `reg` from the mask. It is same as IsRegNumInMask(reg) except +// that it takes `type` as an argument and adds `reg` to the mask for that type. +// +bool regMaskTP::IsRegNumInMask(regNumber reg, var_types type) const +{ + return (low & genSingleTypeRegMask(reg, type)) != RBM_NONE; +} +#endif + //------------------------------------------------------------------------ -// RemoveRegNumFromMask: Removes `reg` from the mask +// AddGprRegs: Adds gprRegs to the mask // // Parameters: -// reg - Register to remove +// gprRegs - Register to check // -void regMaskTP::RemoveRegNumFromMask(regNumber reg) +void regMaskTP::AddGprRegs(SingleTypeRegSet gprRegs) +{ + assert((gprRegs == RBM_NONE) || ((gprRegs & RBM_ALLINT) != RBM_NONE)); + low |= gprRegs; +} + +//------------------------------------------------------------------------ +// AddRegNum: This is similar to AddRegNumInMask(reg, regType) for all platforms +// except Arm. For Arm, it calls getSingleTypeRegMask() instead of genSingleTypeRegMask() +// to create a mask that needs to be added. +// +// Parameters: +// reg - Register to check +// type - type of register +// +void regMaskTP::AddRegNum(regNumber reg, var_types type) { - low &= ~genRegMask(reg); +#ifdef TARGET_ARM + low |= getSingleTypeRegMask(reg, type); +#else + AddRegNumInMask(reg); +#endif +} + +//------------------------------------------------------------------------ +// AddRegsetForType: Add regs of `type` in mask. +// +// Parameters: +// regsToAdd - Register to check +// type - type of register +// +void regMaskTP::AddRegsetForType(SingleTypeRegSet regsToAdd, var_types type) +{ +#ifdef HAS_MORE_THAN_64_REGISTERS + if (!varTypeIsMask(type)) + { + low |= regsToAdd; + } + else + { + high |= regsToAdd; + } +#else + low |= regsToAdd; +#endif +} + +//------------------------------------------------------------------------ +// GetRegSetForType: Get regset for given `type` +// +// Parameters: +// type - type of register +// +// Return: The register set of given type +// +SingleTypeRegSet regMaskTP::GetRegSetForType(var_types type) const +{ +#ifdef HAS_MORE_THAN_64_REGISTERS + if (!varTypeIsMask(type)) + { + return low; + } + else + { + return high; + } +#else + return low; +#endif } //------------------------------------------------------------------------ @@ -22,7 +137,85 @@ void regMaskTP::RemoveRegNumFromMask(regNumber reg) // Parameters: // reg - Register to check // -bool regMaskTP::IsRegNumInMask(regNumber reg) +bool regMaskTP::IsRegNumInMask(regNumber reg) const +{ + SingleTypeRegSet value = genSingleTypeRegMask(reg); +#ifdef HAS_MORE_THAN_64_REGISTERS + if (reg < 64) + { + return (low & value) != RBM_NONE; + } + else + { + return (high & value) != RBM_NONE; + } +#else + return (low & value) != RBM_NONE; +#endif +} + +// This is similar to IsRegNumInMask(reg, regType) for all platforms +// except Arm. For Arm, it calls getSingleTypeRegMask() instead of genSingleTypeRegMask() +// to create a mask that needs to be added. +bool regMaskTP::IsRegNumPresent(regNumber reg, var_types type) const +{ +#ifdef TARGET_ARM + return (low & getSingleTypeRegMask(reg, type)) != RBM_NONE; +#else + return IsRegNumInMask(reg); +#endif +} + +// RemoveRegNumFromMask: Removes `reg` from the mask +// +// Parameters: +// reg - Register to remove +// +void regMaskTP::RemoveRegNumFromMask(regNumber reg) +{ + SingleTypeRegSet value = genSingleTypeRegMask(reg); +#ifdef HAS_MORE_THAN_64_REGISTERS + if (reg < 64) + { + low &= ~value; + } + else + { + high &= ~value; + } +#else + low &= ~value; +#endif +} + +//------------------------------------------------------------------------ +// RemoveRegNum: his is similar to RemoveRegNumFromMask(reg, regType) for all platforms +// except Arm. For Arm, it calls getSingleTypeRegMask() instead of genSingleTypeRegMask() +// to create a mask that needs to be added. +// Parameters: +// reg - Register to remove +// +void regMaskTP::RemoveRegNum(regNumber reg, var_types type) +{ +#ifdef TARGET_ARM + low &= ~getSingleTypeRegMask(reg, type); +#else + RemoveRegNumFromMask(reg); +#endif +} + +void regMaskTP::RemoveRegsetForType(SingleTypeRegSet regsToRemove, var_types type) { - return (low & genRegMask(reg)) != 0; +#ifdef HAS_MORE_THAN_64_REGISTERS + if (!varTypeIsMask(type)) + { + low &= ~regsToRemove; + } + else + { + high &= ~regsToRemove; + } +#else + low &= ~regsToRemove; +#endif } diff --git a/src/coreclr/jit/regset.h b/src/coreclr/jit/regset.h index 99c7f8be6bc55..49cd4dd2e6ecd 100644 --- a/src/coreclr/jit/regset.h +++ b/src/coreclr/jit/regset.h @@ -119,7 +119,7 @@ class RegSet bool rsRegsModified(regMaskTP mask) const { assert(rsModifiedRegsMaskInitialized); - return (rsModifiedRegsMask & mask) != 0; + return (rsModifiedRegsMask & mask).IsNonEmpty(); } void verifyRegUsed(regNumber reg); diff --git a/src/coreclr/jit/scev.cpp b/src/coreclr/jit/scev.cpp index ef3f86b0f133b..e1c65aa11fbc2 100644 --- a/src/coreclr/jit/scev.cpp +++ b/src/coreclr/jit/scev.cpp @@ -127,7 +127,14 @@ void Scev::Dump(Compiler* comp) case ScevOper::Constant: { ScevConstant* cns = (ScevConstant*)this; - printf("%zd", (ssize_t)cns->Value); + if (genTypeSize(cns->Type) == 4) + { + printf("%d", (int32_t)cns->Value); + } + else + { + printf("%lld", (int64_t)cns->Value); + } break; } case ScevOper::Local: @@ -195,6 +202,24 @@ void Scev::Dump(Compiler* comp) } #endif +//------------------------------------------------------------------------ +// Scev::IsInvariant: Check if the SCEV node is invariant inside the loop. +// +// Returns: +// True if so. +// +// Remarks: +// A SCEV is variant if it contains any add recurrence. +// +bool Scev::IsInvariant() +{ + ScevVisit result = Visit([](Scev* scev) { + return scev->OperIs(ScevOper::AddRec) ? ScevVisit::Abort : ScevVisit::Continue; + }); + + return result != ScevVisit::Abort; +} + //------------------------------------------------------------------------ // ScalarEvolutionContext: Construct an instance of a context to do scalar evolution in. // @@ -905,10 +930,20 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) switch (scev->Oper) { case ScevOper::Constant: - case ScevOper::Local: { return scev; } + case ScevOper::Local: + { + ScevLocal* local = (ScevLocal*)scev; + int64_t cns; + if (local->GetConstantValue(m_comp, &cns)) + { + return NewConstant(local->Type, cns); + } + + return local; + } case ScevOper::ZeroExtend: case ScevOper::SignExtend: { @@ -931,16 +966,6 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) : (int64_t)(int32_t)cns->Value); } - if (op1->OperIs(ScevOper::AddRec)) - { - // TODO-Cleanup: This requires some proof that it is ok, but - // currently we do not rely on this. - ScevAddRec* addRec = (ScevAddRec*)op1; - Scev* newStart = Simplify(NewExtension(unop->Oper, TYP_LONG, addRec->Start)); - Scev* newStep = Simplify(NewExtension(unop->Oper, TYP_LONG, addRec->Step)); - return NewAddRec(newStart, newStep); - } - return (op1 == unop->Op1) ? unop : NewExtension(unop->Oper, unop->Type, op1); } case ScevOper::Add: @@ -982,19 +1007,84 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) ScevConstant* cns1 = (ScevConstant*)op1; ScevConstant* cns2 = (ScevConstant*)op2; int64_t newValue; - if (binop->TypeIs(TYP_INT)) + if (genTypeSize(binop->Type) == 4) { - newValue = FoldBinop(binop->Oper, static_cast(cns1->Value), - static_cast(cns2->Value)); + newValue = FoldBinop(binop->Oper, static_cast(cns1->Value), + static_cast(cns2->Value)); } else { - assert(binop->TypeIs(TYP_LONG)); - newValue = FoldBinop(binop->Oper, cns1->Value, cns2->Value); + assert(genTypeSize(binop->Type) == 8); + newValue = FoldBinop(binop->Oper, static_cast(cns1->Value), + static_cast(cns2->Value)); } return NewConstant(binop->Type, newValue); } + else if (op2->OperIs(ScevOper::Constant)) + { + ScevConstant* cns2 = (ScevConstant*)op2; + // a +/<< 0 => a + if (binop->OperIs(ScevOper::Add, ScevOper::Lsh) && (cns2->Value == 0)) + { + return op1; + } + + if (binop->OperIs(ScevOper::Add)) + { + // (a + c1) + c2 => a + (c1 + c2) + if (op1->OperIs(ScevOper::Add) && (((ScevBinop*)op1)->Op2->OperIs(ScevOper::Constant))) + { + ScevBinop* newOp2 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op2, cns2); + ScevBinop* newAdd = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op1, newOp2); + return Simplify(newAdd); + } + } + + if (binop->OperIs(ScevOper::Mul)) + { + // a * 0 => 0 + if (cns2->Value == 0) + { + return cns2; + } + + // a * 1 => a + if (cns2->Value == 1) + { + return op1; + } + + // (a * c1) * c2 => a * (c1 * c2) + if (op1->OperIs(ScevOper::Mul) && (((ScevBinop*)op1)->Op2->OperIs(ScevOper::Constant))) + { + ScevBinop* newOp2 = NewBinop(ScevOper::Mul, ((ScevBinop*)op1)->Op2, cns2); + ScevBinop* newMul = NewBinop(ScevOper::Mul, ((ScevBinop*)op1)->Op1, newOp2); + return Simplify(newMul); + } + } + } + else if (op1->OperIs(ScevOper::Constant)) + { + ScevConstant* cns1 = (ScevConstant*)op1; + if (binop->OperIs(ScevOper::Lsh) && (cns1->Value == 0)) + { + return cns1; + } + } + + if (binop->OperIs(ScevOper::Add)) + { + // (a + c1) + (b + c2) => (a + b) + (c1 + c2) + if (op1->OperIs(ScevOper::Add) && ((ScevBinop*)op1)->Op2->OperIs(ScevOper::Constant) && + op2->OperIs(ScevOper::Add) && ((ScevBinop*)op2)->Op2->OperIs(ScevOper::Constant)) + { + ScevBinop* newOp1 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op1, ((ScevBinop*)op2)->Op1); + ScevBinop* newOp2 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op2, ((ScevBinop*)op2)->Op2); + ScevBinop* newAdd = NewBinop(ScevOper::Add, newOp1, newOp2); + return Simplify(newAdd); + } + } return (op1 == binop->Op1) && (op2 == binop->Op2) ? binop : NewBinop(binop->Oper, op1, op2); } @@ -1009,3 +1099,665 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) unreached(); } } + +//------------------------------------------------------------------------ +// Materialize: Materialize a SCEV into IR and/or a value number. +// +// Parameters: +// scev - The SCEV +// createIR - Whether to create IR. If so "result" will be assigned. +// result - [out] The IR node result. +// resultVN - [out] The VN result. Cannot be nullptr. +// +// Returns: +// True on success. Add recurrences cannot be materialized. +// +bool ScalarEvolutionContext::Materialize(Scev* scev, bool createIR, GenTree** result, ValueNum* resultVN) +{ + switch (scev->Oper) + { + case ScevOper::Constant: + { + ScevConstant* cns = (ScevConstant*)scev; + *resultVN = m_comp->vnStore->VNForGenericCon(scev->Type, reinterpret_cast(&cns->Value)); + if (createIR) + { + if (scev->TypeIs(TYP_LONG)) + { + *result = m_comp->gtNewLconNode(cns->Value); + } + else + { + *result = m_comp->gtNewIconNode((ssize_t)cns->Value, scev->Type); + } + } + + break; + } + case ScevOper::Local: + { + ScevLocal* lcl = (ScevLocal*)scev; + LclVarDsc* dsc = m_comp->lvaGetDesc(lcl->LclNum); + LclSsaVarDsc* ssaDsc = dsc->GetPerSsaData(lcl->SsaNum); + // TODO: To match RBO, but RBO should not use liberal VNs + *resultVN = m_comp->vnStore->VNLiberalNormalValue(ssaDsc->m_vnPair); + + if (createIR) + { + *result = m_comp->gtNewLclvNode(((ScevLocal*)scev)->LclNum, scev->Type); + } + + break; + } + case ScevOper::ZeroExtend: + case ScevOper::SignExtend: + { + ScevUnop* ext = (ScevUnop*)scev; + GenTree* op = nullptr; + ValueNum opVN; + if (!Materialize(ext->Op1, createIR, &op, &opVN)) + { + return false; + } + + *resultVN = m_comp->vnStore->VNForCast(opVN, TYP_LONG, ext->Type, scev->OperIs(ScevOper::ZeroExtend)); + if (createIR) + { + *result = m_comp->gtNewCastNode(ext->Type, op, scev->OperIs(ScevOper::ZeroExtend), TYP_LONG); + } + + break; + } + case ScevOper::Add: + case ScevOper::Mul: + case ScevOper::Lsh: + { + ScevBinop* binop = (ScevBinop*)scev; + GenTree* op1 = nullptr; + ValueNum op1VN; + GenTree* op2 = nullptr; + ValueNum op2VN; + if (!Materialize(binop->Op1, createIR, &op1, &op1VN) || !Materialize(binop->Op2, createIR, &op2, &op2VN)) + { + return false; + } + + genTreeOps oper; + switch (scev->Oper) + { + case ScevOper::Add: + oper = GT_ADD; + break; + case ScevOper::Mul: + oper = GT_MUL; + break; + case ScevOper::Lsh: + oper = GT_LSH; + break; + default: + unreached(); + } + + *resultVN = m_comp->vnStore->VNForFunc(binop->Type, VNFunc(oper), op1VN, op2VN); + if (createIR) + { + if (oper == GT_MUL) + { + if (op1->IsIntegralConst(-1)) + { + *result = m_comp->gtNewOperNode(GT_NEG, op2->TypeGet(), op2); + break; + } + if (op2->IsIntegralConst(-1)) + { + *result = m_comp->gtNewOperNode(GT_NEG, op1->TypeGet(), op1); + break; + } + } + +#ifndef TARGET_64BIT + if ((oper == GT_MUL) && binop->TypeIs(TYP_LONG)) + { + // These require helper calls. Just don't bother. + return false; + } +#endif + *result = m_comp->gtNewOperNode(oper, binop->Type, op1, op2); + } + + break; + } + case ScevOper::AddRec: + return false; + default: + unreached(); + } + + if (createIR) + { + (*result)->SetVNs(ValueNumPair(*resultVN, *resultVN)); + } + + return true; +} + +//------------------------------------------------------------------------ +// Materialize: Materialize a SCEV into IR. +// +// Parameters: +// scev - The SCEV +// +// Returns: +// The node, or nullptr if the SCEV cannot be materialized to IR. +// +GenTree* ScalarEvolutionContext::Materialize(Scev* scev) +{ + ValueNum vn; + GenTree* result; + return Materialize(scev, true, &result, &vn) ? result : nullptr; +} + +//------------------------------------------------------------------------ +// MaterializeVN: Materialize a SCEV into a VN. +// +// Parameters: +// scev - The SCEV +// +// Returns: +// The VN, or ValueNumStore::NoVN if the SCEV is not representable as a VN. +// +ValueNum ScalarEvolutionContext::MaterializeVN(Scev* scev) +{ + ValueNum vn; + return Materialize(scev, false, nullptr, &vn) ? vn : ValueNumStore::NoVN; +} + +//------------------------------------------------------------------------ +// RelopEvaluationResultString: Convert a RelopEvaluationResult to a string. +// +// Parameters: +// result - The evaluation result +// +// Returns: +// String representation +// +static const char* RelopEvaluationResultString(RelopEvaluationResult result) +{ + switch (result) + { + case RelopEvaluationResult::Unknown: + return "unknown"; + case RelopEvaluationResult::True: + return "true"; + case RelopEvaluationResult::False: + return "false"; + default: + return "n/a"; + } +} + +//------------------------------------------------------------------------ +// EvaluateRelop: +// Try to evaluate a relop represented by a VN. +// +// Parameters: +// vn - The VN representing the relop. +// +// Returns: +// The result of the evaluation, or RelopEvaluationResult::Unknown if the +// result is not known. +// +// Remarks: +// Utilizes RBO's reasoning to try to prove the direction of symbolic VNs. +// This function will build dominators if necessary. +// +RelopEvaluationResult ScalarEvolutionContext::EvaluateRelop(ValueNum vn) +{ + if (m_comp->vnStore->IsVNConstant(vn)) + { + assert(m_comp->vnStore->TypeOfVN(vn) == TYP_INT); + return m_comp->vnStore->ConstantValue(vn) != 0 ? RelopEvaluationResult::True + : RelopEvaluationResult::False; + } + + // Evaluate by using dominators and RBO's logic. + // + // TODO-CQ: Using assertions could be stronger given its dataflow, but it + // is not convenient to use (optVNConstantPropOnJTrue does not actually + // make any use of assertions to evaluate conditionals, so it seems like + // the logic does not actually exist anywhere.) + // + if (m_comp->m_domTree == nullptr) + { + m_comp->m_domTree = FlowGraphDominatorTree::Build(m_comp->m_dfsTree); + } + + for (BasicBlock* idom = m_loop->GetHeader()->bbIDom; idom != nullptr; idom = idom->bbIDom) + { + if (!idom->KindIs(BBJ_COND)) + { + continue; + } + + Statement* const domJumpStmt = idom->lastStmt(); + GenTree* const domJumpTree = domJumpStmt->GetRootNode(); + assert(domJumpTree->OperIs(GT_JTRUE)); + GenTree* const domCmpTree = domJumpTree->AsOp()->gtGetOp1(); + + if (!domCmpTree->OperIsCompare()) + { + continue; + } + + // We can use liberal VNs here, as bounds checks are not yet + // manifest explicitly as relops. + // + RelopImplicationInfo rii; + rii.treeNormVN = vn; + rii.domCmpNormVN = m_comp->vnStore->VNLiberalNormalValue(domCmpTree->gtVNPair); + + m_comp->optRelopImpliesRelop(&rii); + + if (!rii.canInfer) + { + continue; + } + + bool domIsInferredRelop = (rii.vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Inferred); + bool domIsSameRelop = (rii.vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Same) || + (rii.vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Swap); + + bool trueReaches = m_comp->optReachable(idom->GetTrueTarget(), m_loop->GetHeader(), idom); + bool falseReaches = m_comp->optReachable(idom->GetFalseTarget(), m_loop->GetHeader(), idom); + + if (trueReaches && !falseReaches && rii.canInferFromTrue) + { + bool relopIsTrue = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop); + return relopIsTrue ? RelopEvaluationResult::True : RelopEvaluationResult::False; + } + + if (falseReaches && !trueReaches && rii.canInferFromFalse) + { + bool relopIsFalse = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop); + return relopIsFalse ? RelopEvaluationResult::False : RelopEvaluationResult::True; + } + } + + return RelopEvaluationResult::Unknown; +} + +//------------------------------------------------------------------------ +// MayOverflowBeforeExit: +// Check if an add recurrence may overflow its computation type before an +// exit condition returns true. +// +// Parameters: +// lhs - The LHS of an expression causing the loop to exit. An add recurrence. +// rhs - The RHS of the expression causing the loop to exit. +// exitOp - The relop such that when [lhs] [exitOp] [rhs] evaluates to true, the loop exits. +// +// Returns: +// True if it is possible for the LHS to overflow its computation type without the loop exiting. +// False if we were able to prove that it cannot. +// +// Remarks: +// May return true conservatively. +// +bool ScalarEvolutionContext::MayOverflowBeforeExit(ScevAddRec* lhs, Scev* rhs, VNFunc exitOp) +{ + int64_t stepCns; + if (!lhs->Step->GetConstantValue(m_comp, &stepCns)) + { + // TODO-CQ: With divisibility checks we can likely handle some of + // these. + return true; + } + + // Handle odd cases, where we count in the other direction of the test, and + // thus only exit after overflow (or immediately). + // + // TODO-CQ: One potential pattern we ought to be able to handle for + // downwards counting loops with unsigned indices is: + // for (uint i = (uint)arr.Length - 1u; i < (uint)arr.Length; i--) + // This one overflows, but on overflow it immediately exits. + // + switch (exitOp) + { + case VNF_GE: + case VNF_GT: + case VNF_GE_UN: + case VNF_GT_UN: + if (stepCns < 0) + { + return true; + } + + break; + case VNF_LE: + case VNF_LT: + case VNF_LE_UN: + case VNF_LT_UN: + if (stepCns > 0) + { + return true; + } + + break; + default: + unreached(); + } + + // A step count of 1/-1 will always exit for "or equal" checks. + if ((stepCns == 1) && ((exitOp == VNFunc(GT_GE)) || (exitOp == VNF_GE_UN))) + { + return false; + } + + if ((stepCns == -1) && ((exitOp == VNFunc(GT_LE)) || (exitOp == VNF_LE_UN))) + { + return false; + } + + // Example: If exitOp is GT_GT then it means that we exit when iv > + // limitCns. In the worst case the IV ends up at limitCns and then steps + // once more. If that still causes us to exit then no overflow is possible. + Scev* step = lhs->Step; + if ((exitOp == VNF_GE) || (exitOp == VNF_GE_UN)) + { + // Exit on iv >= limitCns, so in worst case we are at limitCns - 1. Include the -1 in the step. + Scev* negOne = NewConstant(rhs->Type, -1); + step = NewBinop(ScevOper::Add, step, negOne); + } + else if ((exitOp == VNF_LE) || (exitOp == VNF_LE_UN)) + { + // Exit on iv <= limitCns, so in worst case we are at limitCns + 1. Include the +1 in the step. + Scev* posOne = NewConstant(rhs->Type, 1); + step = NewBinop(ScevOper::Add, step, posOne); + } + + Scev* steppedVal = NewBinop(ScevOper::Add, rhs, step); + steppedVal = Simplify(steppedVal); + ValueNum steppedValVN = MaterializeVN(steppedVal); + + ValueNum rhsVN = MaterializeVN(rhs); + ValueNum relop = m_comp->vnStore->VNForFunc(TYP_INT, exitOp, steppedValVN, rhsVN); + RelopEvaluationResult result = EvaluateRelop(relop); + return result != RelopEvaluationResult::True; +} + +//------------------------------------------------------------------------ +// MapRelopToVNFunc: +// Given a potentially unsigned IR relop, map it to a VNFunc. +// +// Parameters: +// oper - The IR oper. +// isUnsigned - Whether or not this is an unsigned relop. +// +// Returns: +// The VNFunc for the (potentially unsigned) relop. +// +VNFunc ScalarEvolutionContext::MapRelopToVNFunc(genTreeOps oper, bool isUnsigned) +{ + if (isUnsigned) + { + switch (oper) + { + case GT_EQ: + case GT_NE: + return VNFunc(oper); + case GT_LT: + return VNF_LT_UN; + case GT_LE: + return VNF_LE_UN; + case GT_GT: + return VNF_GT_UN; + case GT_GE: + return VNF_GE_UN; + default: + unreached(); + } + } + else + { + return VNFunc(oper); + } +} + +//------------------------------------------------------------------------ +// ComputeExitNotTakenCount: +// Given an exiting basic block, try to compute an exact expression for the +// number of times the exit is not taken, before it is taken. +// +// Parameters: +// exiting - The exiting basic block. Must be a BBJ_COND block. +// +// Returns: +// A SCEV representing the number of times the exit is _not_ taken, assuming +// it runs on every iteration of the loop. Returns nullptr if an exact count +// cannot be computed. +// +// Remarks: +// The SCEV returned here is equal to the backedge count when the exiting +// block dominates all backedges and when it is the only exit of the loop. +// +// The backedge count of the loop is defined as the number of times the +// header block is entered from a backedge. It follows that the number of +// times the header block is entered is the backedge count + 1. This quantity +// is typically called the trip count. +// +// The backedge count gives insight about add recurrences in the loop, since +// it is the number of times every add recurrence steps. The final value of +// an add recurrence is thus (start + step * ). +// +// Loops for which the backedge/trip count can be computed are called counted +// loops. +// +Scev* ScalarEvolutionContext::ComputeExitNotTakenCount(BasicBlock* exiting) +{ + assert(exiting->KindIs(BBJ_COND)); + assert(m_loop->ContainsBlock(exiting->GetTrueTarget()) != m_loop->ContainsBlock(exiting->GetFalseTarget())); + + Statement* lastStmt = exiting->lastStmt(); + GenTree* lastExpr = lastStmt->GetRootNode(); + assert(lastExpr->OperIs(GT_JTRUE)); + GenTree* cond = lastExpr->gtGetOp1(); + + if (!cond->OperIs(GT_LT, GT_LE, GT_GT, GT_GE)) + { + // TODO-CQ: We can handle EQ/NE with divisibility checks. + return nullptr; + } + + if (!varTypeIsIntegralOrI(cond->gtGetOp1())) + { + return nullptr; + } + + Scev* op1 = Analyze(exiting, cond->gtGetOp1()); + Scev* op2 = Analyze(exiting, cond->gtGetOp2()); + + if ((op1 == nullptr) || (op2 == nullptr)) + { + return nullptr; + } + + if (varTypeIsGC(op1->Type) || varTypeIsGC(op2->Type)) + { + // TODO-CQ: Add SUB operator + return nullptr; + } + + // Now phrase the test such that the loop is exited when [lhs] >/>= [rhs]. + Scev* lhs = Simplify(op1); + Scev* rhs = Simplify(op2); + genTreeOps exitOp = cond->gtOper; + if (!m_loop->ContainsBlock(exiting->GetFalseTarget())) + { + // We exit in the false case, so we exit when the oper is the reverse. + exitOp = GenTree::ReverseRelop(exitOp); + } + + // We require an add recurrence to have been exposed at this point. + if (!lhs->OperIs(ScevOper::AddRec) && !rhs->OperIs(ScevOper::AddRec)) + { + // If both are invariant we could still handle some cases here (it will + // be 0 or infinite). Probably uncommon. + return nullptr; + } + + // Now normalize variant SCEV to the left. + bool lhsInvariant = lhs->IsInvariant(); + bool rhsInvariant = rhs->IsInvariant(); + if (lhsInvariant == rhsInvariant) + { + // Both variant. Here we could also prove also try to prove some cases, + // but again this is expected to be uncommon. + return nullptr; + } + + if (lhsInvariant) + { + exitOp = GenTree::SwapRelop(exitOp); + std::swap(lhs, rhs); + } + + assert(lhs->OperIs(ScevOper::AddRec)); + +#ifdef DEBUG + if (m_comp->verbose) + { + printf(" " FMT_LP " exits when:\n ", m_loop->GetIndex()); + lhs->Dump(m_comp); + const char* exitOpStr = ""; + switch (exitOp) + { + case GT_LT: + exitOpStr = "<"; + break; + case GT_LE: + exitOpStr = "<="; + break; + case GT_GT: + exitOpStr = ">"; + break; + case GT_GE: + exitOpStr = ">="; + break; + default: + unreached(); + } + printf(" %s ", exitOpStr); + rhs->Dump(m_comp); + printf("\n"); + } +#endif + + VNFunc exitOpVNF = MapRelopToVNFunc(exitOp, cond->IsUnsigned()); + if (MayOverflowBeforeExit((ScevAddRec*)lhs, rhs, exitOpVNF)) + { + JITDUMP(" May overflow, cannot determine backedge count\n"); + return nullptr; + } + + JITDUMP(" Does not overflow past the test\n"); + + // We have lhs [exitOp] rhs, where lhs is an add rec + Scev* lowerBound; + Scev* upperBound; + Scev* divisor; + switch (exitOpVNF) + { + case VNF_GE: + case VNF_GE_UN: + { + // Exit on >= rhs. + // Trip count expression is ceil((rhs - start) / step) = (rhs + (step - 1) - start) / step. + Scev* stepNegOne = NewBinop(ScevOper::Add, ((ScevAddRec*)lhs)->Step, NewConstant(rhs->Type, -1)); + Scev* rhsWithStep = NewBinop(ScevOper::Add, rhs, stepNegOne); + lowerBound = ((ScevAddRec*)lhs)->Start; + upperBound = rhsWithStep; + divisor = ((ScevAddRec*)lhs)->Step; + break; + } + case VNF_GT: + case VNF_GT_UN: + { + // Exit on > rhs. + // Trip count expression is ceil((rhs + 1 - start) / step) = (rhs + step - start) / step. + lowerBound = ((ScevAddRec*)lhs)->Start; + upperBound = NewBinop(ScevOper::Add, rhs, ((ScevAddRec*)lhs)->Step); + divisor = ((ScevAddRec*)lhs)->Step; + break; + } + case VNF_LE: + case VNF_LE_UN: + { + // Exit on <= rhs. + // Trip count expression is ceil((start - rhs) / -step) = (start + (-step - 1) - rhs) / -step + // = (start - step - 1 - rhs) / -step = start - (rhs + step + 1) / -step. + Scev* stepPlusOne = NewBinop(ScevOper::Add, ((ScevAddRec*)lhs)->Step, NewConstant(rhs->Type, 1)); + Scev* rhsWithStep = NewBinop(ScevOper::Add, rhs, stepPlusOne); + lowerBound = rhsWithStep; + upperBound = ((ScevAddRec*)lhs)->Start; + divisor = NewBinop(ScevOper::Mul, ((ScevAddRec*)lhs)->Step, NewConstant(lhs->Type, -1)); + break; + } + case VNF_LT: + case VNF_LT_UN: + { + // Exit on < rhs. + // Trip count expression is ceil((start - (rhs - 1)) / -step) = (start + (-step - 1) - (rhs - 1)) / -step + // = (start - (rhs + step)) / -step. + lowerBound = NewBinop(ScevOper::Add, rhs, ((ScevAddRec*)lhs)->Step); + upperBound = ((ScevAddRec*)lhs)->Start; + divisor = NewBinop(ScevOper::Mul, ((ScevAddRec*)lhs)->Step, NewConstant(lhs->Type, -1)); + break; + } + default: + unreached(); + } + + lowerBound = Simplify(lowerBound); + upperBound = Simplify(upperBound); + + // Now prove that the lower bound is indeed a lower bound. + JITDUMP(" Need to prove "); + DBEXEC(VERBOSE, lowerBound->Dump(m_comp)); + JITDUMP(" <= "); + DBEXEC(VERBOSE, upperBound->Dump(m_comp)); + + VNFunc relopFunc = ValueNumStore::VNFuncIsSignedComparison(exitOpVNF) ? VNF_LE : VNF_LE_UN; + ValueNum relop = + m_comp->vnStore->VNForFunc(TYP_INT, relopFunc, MaterializeVN(lowerBound), MaterializeVN(upperBound)); + RelopEvaluationResult result = EvaluateRelop(relop); + JITDUMP(": %s\n", RelopEvaluationResultString(result)); + + if (result != RelopEvaluationResult::True) + { + return nullptr; + } + + divisor = Simplify(divisor); + int64_t divisorVal; + if (!divisor->GetConstantValue(m_comp, &divisorVal) || ((divisorVal != 1) && (divisorVal != -1))) + { + // TODO-CQ: Enable. Likely need to add a division operator to SCEV. + return nullptr; + } + + Scev* backedgeCountSubtraction = + NewBinop(ScevOper::Add, upperBound, NewBinop(ScevOper::Mul, lowerBound, NewConstant(lowerBound->Type, -1))); + Scev* backedgeCount = backedgeCountSubtraction; + if (divisorVal == -1) + { + backedgeCount = NewBinop(ScevOper::Mul, backedgeCount, NewConstant(backedgeCount->Type, -1)); + } + + backedgeCount = Simplify(backedgeCount); + JITDUMP(" Backedge count: "); + DBEXEC(VERBOSE, backedgeCount->Dump(m_comp)); + JITDUMP("\n"); + + return backedgeCount; +} diff --git a/src/coreclr/jit/scev.h b/src/coreclr/jit/scev.h index 1aab39e3d3a5d..cc4bcb6cfb4ac 100644 --- a/src/coreclr/jit/scev.h +++ b/src/coreclr/jit/scev.h @@ -72,6 +72,8 @@ struct Scev #endif template ScevVisit Visit(TVisitor visitor); + + bool IsInvariant(); }; struct ScevConstant : Scev @@ -190,6 +192,13 @@ ScevVisit Scev::Visit(TVisitor visitor) return ScevVisit::Continue; } +enum class RelopEvaluationResult +{ + Unknown, + True, + False, +}; + typedef JitHashTable, Scev*> ScalarEvolutionMap; // Scalar evolution is analyzed in the context of a single loop, and are @@ -218,6 +227,11 @@ class ScalarEvolutionContext Scev* CreateScevForConstant(GenTreeIntConCommon* tree); void ExtractAddOperands(ScevBinop* add, ArrayStack& operands); + VNFunc MapRelopToVNFunc(genTreeOps oper, bool isUnsigned); + RelopEvaluationResult EvaluateRelop(ValueNum relop); + bool MayOverflowBeforeExit(ScevAddRec* lhs, Scev* rhs, VNFunc exitOp); + + bool Materialize(Scev* scev, bool createIR, GenTree** result, ValueNum* resultVN); public: ScalarEvolutionContext(Compiler* comp); @@ -231,4 +245,9 @@ class ScalarEvolutionContext Scev* Analyze(BasicBlock* block, GenTree* tree); Scev* Simplify(Scev* scev); + + Scev* ComputeExitNotTakenCount(BasicBlock* exiting); + + GenTree* Materialize(Scev* scev); + ValueNum MaterializeVN(Scev* scev); }; diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index f2df3915088c8..c7ae69f4ca1e2 100644 --- a/src/coreclr/jit/simdashwintrinsic.cpp +++ b/src/coreclr/jit/simdashwintrinsic.cpp @@ -512,8 +512,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 - bool isOpExplicit = false; - switch (intrinsic) { case NI_VectorT_ConvertToInt32Native: @@ -546,7 +544,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_MultiplyAddEstimate: case NI_Vector3_MultiplyAddEstimate: - case NI_Vector4_MultiplyAddEstimate: case NI_VectorT_MultiplyAddEstimate: { if (BlockNonDeterministicIntrinsics(mustExpand)) @@ -632,36 +629,7 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_VectorT_As: - case NI_VectorT_AsVectorByte: - case NI_VectorT_AsVectorDouble: - case NI_VectorT_AsVectorInt16: - case NI_VectorT_AsVectorInt32: - case NI_VectorT_AsVectorInt64: - case NI_VectorT_AsVectorNInt: - case NI_VectorT_AsVectorNUInt: - case NI_VectorT_AsVectorSByte: - case NI_VectorT_AsVectorSingle: - case NI_VectorT_AsVectorUInt16: - case NI_VectorT_AsVectorUInt32: - case NI_VectorT_AsVectorUInt64: - { - unsigned retSimdSize; - CorInfoType retBaseJitType = getBaseJitTypeAndSizeOfSIMDType(sig->retTypeSigClass, &retSimdSize); - - if ((retBaseJitType == CORINFO_TYPE_UNDEF) || - !varTypeIsArithmetic(JitType2PreciseVarType(retBaseJitType)) || (retSimdSize == 0)) - { - // We get here if the return type is an unsupported type - return nullptr; - } - - isOpExplicit = true; - break; - } - #if defined(TARGET_XARCH) - case NI_VectorT_get_Item: case NI_VectorT_GetElement: { op2 = impStackTop(0).val; @@ -726,10 +694,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_Quaternion_WithElement: case NI_Vector2_WithElement: case NI_Vector3_WithElement: - case NI_Vector4_WithElement: case NI_VectorT_WithElement: { assert(sig->numArgs == 3); @@ -810,10 +776,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_Quaternion_WithElement: case NI_Vector2_WithElement: case NI_Vector3_WithElement: - case NI_Vector4_WithElement: case NI_VectorT_WithElement: { assert(numArgs == 3); @@ -853,7 +817,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_FusedMultiplyAdd: case NI_Vector3_FusedMultiplyAdd: - case NI_Vector4_FusedMultiplyAdd: case NI_VectorT_FusedMultiplyAdd: { bool isFmaAccelerated = false; @@ -872,7 +835,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, } #if defined(TARGET_XARCH) - case NI_VectorT_Multiply: case NI_VectorT_op_Multiply: { if (varTypeIsLong(simdBaseType)) @@ -893,7 +855,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, #endif // TARGET_XARCH #if defined(TARGET_XARCH) - case NI_VectorT_ShiftRightArithmetic: case NI_VectorT_op_RightShift: { if (varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE)) @@ -937,77 +898,11 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdGetIndicesNode(retType, simdBaseJitType, simdSize); } - case NI_Vector2_get_One: - case NI_Vector3_get_One: - case NI_Vector4_get_One: case NI_VectorT_get_One: { return gtNewOneConNode(retType, simdBaseType); } - case NI_Vector2_get_UnitX: - case NI_Vector3_get_UnitX: - case NI_Vector4_get_UnitX: - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = 1.0f; - vecCon->gtSimdVal.f32[1] = 0.0f; - vecCon->gtSimdVal.f32[2] = 0.0f; - vecCon->gtSimdVal.f32[3] = 0.0f; - - return vecCon; - } - - case NI_Vector2_get_UnitY: - case NI_Vector3_get_UnitY: - case NI_Vector4_get_UnitY: - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = 0.0f; - vecCon->gtSimdVal.f32[1] = 1.0f; - vecCon->gtSimdVal.f32[2] = 0.0f; - vecCon->gtSimdVal.f32[3] = 0.0f; - - return vecCon; - } - - case NI_Vector3_get_UnitZ: - case NI_Vector4_get_UnitZ: - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = 0.0f; - vecCon->gtSimdVal.f32[1] = 0.0f; - vecCon->gtSimdVal.f32[2] = 1.0f; - vecCon->gtSimdVal.f32[3] = 0.0f; - - return vecCon; - } - - case NI_Quaternion_get_Identity: - case NI_Vector4_get_UnitW: - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = 0.0f; - vecCon->gtSimdVal.f32[1] = 0.0f; - vecCon->gtSimdVal.f32[2] = 0.0f; - vecCon->gtSimdVal.f32[3] = 1.0f; - - return vecCon; - } - - case NI_Quaternion_get_Zero: - case NI_Vector2_get_Zero: - case NI_Vector3_get_Zero: - case NI_Vector4_get_Zero: - case NI_VectorT_get_Zero: - { - return gtNewZeroConNode(retType); - } - default: { // Some platforms warn about unhandled switch cases @@ -1022,20 +917,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, { assert(newobjThis == nullptr); - isOpExplicit |= (intrinsic == NI_VectorT_op_Explicit); - - if (isOpExplicit) - { - // We fold away the cast here, as it only exists to satisfy the - // type system. It is safe to do this here since the op1 type - // and the signature return type are both the same TYP_SIMD. - op1 = impSIMDPopStack(); - SetOpLclRelatedToSIMDIntrinsic(op1); - assert(op1->gtType == getSIMDTypeForSize(getSIMDTypeSizeInBytes(sig->retTypeSigClass))); - - return op1; - } - argType = isInstanceMethod ? simdType : JITtype2varType(strip(info.compCompHnd->getArgType(sig, argList, &argClass))); op1 = getArgForHWIntrinsic(argType, argClass, isInstanceMethod); @@ -1044,7 +925,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, { case NI_Vector2_Abs: case NI_Vector3_Abs: - case NI_Vector4_Abs: case NI_VectorT_Abs: { return gtNewSimdAbsNode(retType, op1, simdBaseJitType, simdSize); @@ -1055,75 +935,11 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdCeilNode(retType, op1, simdBaseJitType, simdSize); } - case NI_Quaternion_Conjugate: - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = -1.0f; - vecCon->gtSimdVal.f32[1] = -1.0f; - vecCon->gtSimdVal.f32[2] = -1.0f; - vecCon->gtSimdVal.f32[3] = +1.0f; - - return gtNewSimdBinOpNode(GT_MUL, retType, op1, vecCon, simdBaseJitType, simdSize); - } - case NI_VectorT_Floor: { return gtNewSimdFloorNode(retType, op1, simdBaseJitType, simdSize); } - case NI_Quaternion_Inverse: - { - GenTree* clonedOp1; - op1 = impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for quaternion inverse (1)")); - - GenTree* clonedOp2; - clonedOp1 = impCloneExpr(clonedOp1, &clonedOp2, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for quaternion inverse (2)")); - - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - vecCon->gtSimdVal.f32[0] = -1.0f; - vecCon->gtSimdVal.f32[1] = -1.0f; - vecCon->gtSimdVal.f32[2] = -1.0f; - vecCon->gtSimdVal.f32[3] = +1.0f; - - GenTree* conjugate = gtNewSimdBinOpNode(GT_MUL, retType, op1, vecCon, simdBaseJitType, simdSize); - op1 = gtNewSimdDotProdNode(retType, clonedOp1, clonedOp2, simdBaseJitType, simdSize); - - return gtNewSimdBinOpNode(GT_DIV, retType, conjugate, op1, simdBaseJitType, simdSize); - } - - case NI_Quaternion_Length: - case NI_Vector2_Length: - case NI_Vector3_Length: - case NI_Vector4_Length: - { - GenTree* clonedOp1; - op1 = - impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for vector length")); - - op1 = gtNewSimdDotProdNode(simdType, op1, clonedOp1, simdBaseJitType, simdSize); - op1 = gtNewSimdSqrtNode(simdType, op1, simdBaseJitType, simdSize); - - return gtNewSimdGetElementNode(retType, op1, gtNewIconNode(0), simdBaseJitType, simdSize); - } - - case NI_Quaternion_LengthSquared: - case NI_Vector2_LengthSquared: - case NI_Vector3_LengthSquared: - case NI_Vector4_LengthSquared: - { - GenTree* clonedOp1; - op1 = impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for vector length squared")); - - op1 = gtNewSimdDotProdNode(simdType, op1, clonedOp1, simdBaseJitType, simdSize); - return gtNewSimdGetElementNode(retType, op1, gtNewIconNode(0), simdBaseJitType, simdSize); - } - - case NI_VectorT_Load: case NI_VectorT_LoadUnsafe: { if (op1->OperIs(GT_CAST) && op1->gtGetOp1()->TypeIs(TYP_BYREF)) @@ -1157,40 +973,13 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdLoadNonTemporalNode(retType, op1, simdBaseJitType, simdSize); } - case NI_Quaternion_Negate: - case NI_Quaternion_op_UnaryNegation: - case NI_Vector2_Negate: case NI_Vector2_op_UnaryNegation: - case NI_Vector3_Negate: case NI_Vector3_op_UnaryNegation: - case NI_Vector4_Negate: - case NI_Vector4_op_UnaryNegation: - case NI_VectorT_Negate: case NI_VectorT_op_UnaryNegation: { return gtNewSimdUnOpNode(GT_NEG, retType, op1, simdBaseJitType, simdSize); } - case NI_Quaternion_Normalize: - case NI_Vector2_Normalize: - case NI_Vector3_Normalize: - case NI_Vector4_Normalize: - { - GenTree* clonedOp1; - op1 = impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for vector normalize (1)")); - - GenTree* clonedOp2; - clonedOp1 = impCloneExpr(clonedOp1, &clonedOp2, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for vector normalize (2)")); - - op1 = gtNewSimdDotProdNode(retType, op1, clonedOp1, simdBaseJitType, simdSize); - op1 = gtNewSimdSqrtNode(retType, op1, simdBaseJitType, simdSize); - - return gtNewSimdBinOpNode(GT_DIV, retType, clonedOp2, op1, simdBaseJitType, simdSize); - } - - case NI_VectorT_OnesComplement: case NI_VectorT_op_OnesComplement: { return gtNewSimdUnOpNode(GT_NOT, retType, op1, simdBaseJitType, simdSize); @@ -1198,7 +987,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_Sqrt: case NI_Vector3_Sqrt: - case NI_Vector4_Sqrt: case NI_VectorT_Sqrt: { return gtNewSimdSqrtNode(retType, op1, simdBaseJitType, simdSize); @@ -1222,11 +1010,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdToScalarNode(retType, op1, simdBaseJitType, simdSize); } - case NI_VectorT_op_UnaryPlus: - { - return op1; - } - case NI_VectorT_WidenLower: { return gtNewSimdWidenLowerNode(retType, op1, simdBaseJitType, simdSize); @@ -1416,15 +1199,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, switch (intrinsic) { - case NI_Quaternion_Add: - case NI_Quaternion_op_Addition: - case NI_Vector2_Add: case NI_Vector2_op_Addition: - case NI_Vector3_Add: case NI_Vector3_op_Addition: - case NI_Vector4_Add: - case NI_Vector4_op_Addition: - case NI_VectorT_Add: case NI_VectorT_op_Addition: { return gtNewSimdBinOpNode(GT_ADD, retType, op1, op2, simdBaseJitType, simdSize); @@ -1435,13 +1211,11 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdBinOpNode(GT_AND_NOT, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_BitwiseAnd: case NI_VectorT_op_BitwiseAnd: { return gtNewSimdBinOpNode(GT_AND, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_BitwiseOr: case NI_VectorT_op_BitwiseOr: { return gtNewSimdBinOpNode(GT_OR, retType, op1, op2, simdBaseJitType, simdSize); @@ -1449,7 +1223,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_CreateBroadcast: case NI_Vector3_CreateBroadcast: - case NI_Vector4_CreateBroadcast: case NI_VectorT_CreateBroadcast: { assert(retType == TYP_VOID); @@ -1464,64 +1237,15 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdCreateSequenceNode(simdType, op1, op2, simdBaseJitType, simdSize); } - case NI_Plane_CreateFromVector4: - { - assert(retType == TYP_VOID); - - copyBlkDst = op1; - copyBlkSrc = op2; - - break; - } - - case NI_Vector2_Distance: - case NI_Vector3_Distance: - case NI_Vector4_Distance: - { - op1 = gtNewSimdBinOpNode(GT_SUB, simdType, op1, op2, simdBaseJitType, simdSize); - - GenTree* clonedOp1; - op1 = impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone diff for vector distance")); - - op1 = gtNewSimdDotProdNode(simdType, op1, clonedOp1, simdBaseJitType, simdSize); - op1 = gtNewSimdSqrtNode(simdType, op1, simdBaseJitType, simdSize); - - return gtNewSimdGetElementNode(retType, op1, gtNewIconNode(0), simdBaseJitType, simdSize); - } - - case NI_Vector2_DistanceSquared: - case NI_Vector3_DistanceSquared: - case NI_Vector4_DistanceSquared: - { - op1 = gtNewSimdBinOpNode(GT_SUB, simdType, op1, op2, simdBaseJitType, simdSize); - - GenTree* clonedOp1; - op1 = impCloneExpr(op1, &clonedOp1, CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone diff for vector distance squared")); - - op1 = gtNewSimdDotProdNode(simdType, op1, clonedOp1, simdBaseJitType, simdSize); - return gtNewSimdGetElementNode(retType, op1, gtNewIconNode(0), simdBaseJitType, simdSize); - } - - case NI_Quaternion_Divide: - case NI_Vector2_Divide: case NI_Vector2_op_Division: - case NI_Vector3_Divide: case NI_Vector3_op_Division: - case NI_Vector4_Divide: - case NI_Vector4_op_Division: - case NI_VectorT_Divide: case NI_VectorT_op_Division: { return gtNewSimdBinOpNode(GT_DIV, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_Plane_Dot: - case NI_Quaternion_Dot: case NI_Vector2_Dot: case NI_Vector3_Dot: - case NI_Vector4_Dot: case NI_VectorT_Dot: { op1 = gtNewSimdDotProdNode(simdType, op1, op2, simdBaseJitType, simdSize); @@ -1533,12 +1257,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdCmpOpNode(GT_EQ, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_Plane_op_Equality: - case NI_Quaternion_op_Equality: case NI_Vector2_op_Equality: case NI_Vector3_op_Equality: - case NI_Vector4_op_Equality: - case NI_VectorT_EqualsAll: case NI_VectorT_op_Equality: { return gtNewSimdCmpOpAllNode(GT_EQ, retType, op1, op2, simdBaseJitType, simdSize); @@ -1549,21 +1269,13 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdCmpOpAnyNode(GT_EQ, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_Xor: case NI_VectorT_op_ExclusiveOr: { return gtNewSimdBinOpNode(GT_XOR, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_Quaternion_get_Item: - case NI_Quaternion_GetElement: - case NI_Vector2_get_Item: case NI_Vector2_GetElement: - case NI_Vector3_get_Item: case NI_Vector3_GetElement: - case NI_Vector4_get_Item: - case NI_Vector4_GetElement: - case NI_VectorT_get_Item: case NI_VectorT_GetElement: { return gtNewSimdGetElementNode(retType, op1, op2, simdBaseJitType, simdSize); @@ -1599,11 +1311,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdCmpOpAnyNode(GT_GE, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_Plane_op_Inequality: - case NI_Quaternion_op_Inequality: case NI_Vector2_op_Inequality: case NI_Vector3_op_Inequality: - case NI_Vector4_op_Inequality: case NI_VectorT_op_Inequality: { return gtNewSimdCmpOpAnyNode(GT_NE, retType, op1, op2, simdBaseJitType, simdSize); @@ -1658,7 +1367,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_Max: case NI_Vector3_Max: - case NI_Vector4_Max: case NI_VectorT_Max: { return gtNewSimdMaxNode(retType, op1, op2, simdBaseJitType, simdSize); @@ -1666,21 +1374,13 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_Min: case NI_Vector3_Min: - case NI_Vector4_Min: case NI_VectorT_Min: { return gtNewSimdMinNode(retType, op1, op2, simdBaseJitType, simdSize); } - case NI_Quaternion_Multiply: - case NI_Quaternion_op_Multiply: - case NI_Vector2_Multiply: case NI_Vector2_op_Multiply: - case NI_Vector3_Multiply: case NI_Vector3_op_Multiply: - case NI_Vector4_Multiply: - case NI_Vector4_op_Multiply: - case NI_VectorT_Multiply: case NI_VectorT_op_Multiply: { return gtNewSimdBinOpNode(GT_MUL, retType, op1, op2, simdBaseJitType, simdSize); @@ -1691,26 +1391,22 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_ShiftLeft: case NI_VectorT_op_LeftShift: { return gtNewSimdBinOpNode(GT_LSH, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_ShiftRightArithmetic: case NI_VectorT_op_RightShift: { genTreeOps op = varTypeIsUnsigned(simdBaseType) ? GT_RSZ : GT_RSH; return gtNewSimdBinOpNode(op, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_ShiftRightLogical: case NI_VectorT_op_UnsignedRightShift: { return gtNewSimdBinOpNode(GT_RSZ, retType, op1, op2, simdBaseJitType, simdSize); } - case NI_VectorT_Store: case NI_VectorT_StoreUnsafe: { assert(retType == TYP_VOID); @@ -1750,15 +1446,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, return gtNewSimdStoreNonTemporalNode(op2, op1, simdBaseJitType, simdSize); } - case NI_Quaternion_Subtract: - case NI_Quaternion_op_Subtraction: - case NI_Vector2_Subtract: case NI_Vector2_op_Subtraction: - case NI_Vector3_Subtract: case NI_Vector3_op_Subtraction: - case NI_Vector4_Subtract: - case NI_Vector4_op_Subtraction: - case NI_VectorT_Subtract: case NI_VectorT_op_Subtraction: { return gtNewSimdBinOpNode(GT_SUB, retType, op1, op2, simdBaseJitType, simdSize); @@ -1815,14 +1504,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, switch (intrinsic) { - case NI_Vector2_Clamp: - case NI_Vector3_Clamp: - case NI_Vector4_Clamp: - { - GenTree* maxNode = gtNewSimdMaxNode(retType, op1, op2, simdBaseJitType, simdSize); - return gtNewSimdMinNode(retType, maxNode, op3, simdBaseJitType, simdSize); - } - case NI_VectorT_ConditionalSelect: { return gtNewSimdCndSelNode(retType, op1, op2, op3, simdBaseJitType, simdSize); @@ -1830,7 +1511,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_FusedMultiplyAdd: case NI_Vector3_FusedMultiplyAdd: - case NI_Vector4_FusedMultiplyAdd: case NI_VectorT_FusedMultiplyAdd: { return gtNewSimdFmaNode(retType, op1, op2, op3, simdBaseJitType, simdSize); @@ -1838,7 +1518,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_Lerp: case NI_Vector3_Lerp: - case NI_Vector4_Lerp: { // We generate nodes equivalent to `(op1 * (1.0f - op3)) + (op2 * op3)` // optimizing for xarch by doing a single broadcast and for arm64 by @@ -1879,7 +1558,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_Vector2_MultiplyAddEstimate: case NI_Vector3_MultiplyAddEstimate: - case NI_Vector4_MultiplyAddEstimate: case NI_VectorT_MultiplyAddEstimate: { bool isFmaAccelerated = false; @@ -1963,10 +1641,7 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_Plane_CreateFromVector3: - case NI_Quaternion_CreateFromVector3: case NI_Vector3_CreateFromVector2: - case NI_Vector4_CreateFromVector3: { assert(retType == TYP_VOID); assert(simdBaseType == TYP_FLOAT); @@ -2001,10 +1676,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_Quaternion_WithElement: case NI_Vector2_WithElement: case NI_Vector3_WithElement: - case NI_Vector4_WithElement: case NI_VectorT_WithElement: { return gtNewSimdWithElementNode(retType, op1, op2, op3, simdBaseJitType, simdSize); @@ -2097,133 +1770,6 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, break; } - case NI_Vector4_CreateFromVector2: - { - assert(retType == TYP_VOID); - assert(simdBaseType == TYP_FLOAT); - assert(simdSize == 16); - - // TODO-CQ: We should be able to check for contiguous args here after - // the relevant methods are updated to support more than just float - - if (op2->IsCnsVec() && op3->IsCnsFltOrDbl() && op4->IsCnsFltOrDbl()) - { - GenTreeVecCon* vecCon = op2->AsVecCon(); - vecCon->gtType = simdType; - - vecCon->gtSimdVal.f32[2] = static_cast(op3->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[3] = static_cast(op4->AsDblCon()->DconValue()); - - copyBlkSrc = vecCon; - } - else - { - GenTree* idx = gtNewIconNode(2, TYP_INT); - op2 = gtNewSimdWithElementNode(simdType, op2, idx, op3, simdBaseJitType, simdSize); - - idx = gtNewIconNode(3, TYP_INT); - copyBlkSrc = gtNewSimdWithElementNode(simdType, op2, idx, op4, simdBaseJitType, simdSize); - } - - copyBlkDst = op1; - break; - } - - default: - { - // Some platforms warn about unhandled switch cases - // We handle it more generally via the assert and nullptr return below. - break; - } - } - break; - } - - case 5: - { - assert(isInstanceMethod); - assert(SimdAsHWIntrinsicInfo::SpillSideEffectsOp1(intrinsic)); - assert(!SimdAsHWIntrinsicInfo::SpillSideEffectsOp2(intrinsic)); - - if (newobjThis == nullptr) - { - impSpillSideEffect(true, verCurrentState.esStackDepth - - 5 DEBUGARG("Spilling op1 side effects for SimdAsHWIntrinsic")); - } - - CORINFO_ARG_LIST_HANDLE arg2 = argList; - CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); - CORINFO_ARG_LIST_HANDLE arg4 = info.compCompHnd->getArgNext(arg3); - CORINFO_ARG_LIST_HANDLE arg5 = info.compCompHnd->getArgNext(arg4); - - argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg5, &argClass))); - op5 = getArgForHWIntrinsic(argType, argClass); - - argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg4, &argClass))); - op4 = getArgForHWIntrinsic(argType, argClass); - - argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); - op3 = getArgForHWIntrinsic(argType, argClass); - - argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); - op2 = getArgForHWIntrinsic(argType, argClass); - - if ((newobjThis == nullptr) && (retType == TYP_VOID)) - { - op1 = getArgForHWIntrinsic(TYP_BYREF, argClass, isInstanceMethod, newobjThis); - } - else - { - op1 = getArgForHWIntrinsic(simdType, (newobjThis != nullptr) ? clsHnd : argClass, isInstanceMethod, - newobjThis); - } - - switch (intrinsic) - { - case NI_Plane_Create: - case NI_Quaternion_Create: - case NI_Vector4_Create: - { - assert(retType == TYP_VOID); - assert(simdBaseType == TYP_FLOAT); - assert(simdSize == 16); - - if (op2->IsCnsFltOrDbl() && op3->IsCnsFltOrDbl() && op4->IsCnsFltOrDbl() && op5->IsCnsFltOrDbl()) - { - GenTreeVecCon* vecCon = gtNewVconNode(TYP_SIMD16); - - float cnsVal = 0; - - vecCon->gtSimdVal.f32[0] = static_cast(op2->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[1] = static_cast(op3->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[2] = static_cast(op4->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[3] = static_cast(op5->AsDblCon()->DconValue()); - - copyBlkSrc = vecCon; - } - else if (areArgumentsContiguous(op2, op3) && areArgumentsContiguous(op3, op4) && - areArgumentsContiguous(op4, op5)) - { - GenTree* op2Address = CreateAddressNodeForSimdHWIntrinsicCreate(op2, simdBaseType, 16); - copyBlkSrc = gtNewIndir(TYP_SIMD16, op2Address); - } - else - { - IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), 4); - - nodeBuilder.AddOperand(0, op2); - nodeBuilder.AddOperand(1, op3); - nodeBuilder.AddOperand(2, op4); - nodeBuilder.AddOperand(3, op5); - - copyBlkSrc = gtNewSimdHWIntrinsicNode(TYP_SIMD16, std::move(nodeBuilder), NI_Vector128_Create, - simdBaseJitType, 16); - } - - copyBlkDst = op1; - break; - } - default: { // Some platforms warn about unhandled switch cases diff --git a/src/coreclr/jit/simdashwintrinsiclistarm64.h b/src/coreclr/jit/simdashwintrinsiclistarm64.h index e6f3cc0d6db46..3f73df38f13dd 100644 --- a/src/coreclr/jit/simdashwintrinsiclistarm64.h +++ b/src/coreclr/jit/simdashwintrinsiclistarm64.h @@ -32,78 +32,21 @@ * Each intrinsic has one or more flags with type of `enum SimdAsHWIntrinsicFlag` */ -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Plane Intrinsics -SIMD_AS_HWINTRINSIC_NM(Plane, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Plane, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_NM(Plane, CreateFromVector4, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_CreateFromVector4, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Plane, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Plane, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Plane, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Quaternion Intrinsics -SIMD_AS_HWINTRINSIC_ID(Quaternion, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Conjugate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Conjugate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Quaternion, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Quaternion, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Identity, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Identity, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Item, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Inverse, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Inverse, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Quaternion, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // ISA ID Name NumArg Instructions Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector2 Intrinsics SIMD_AS_HWINTRINSIC_ID(Vector2, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector2, Create, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector2, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(Vector2, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_Item, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector2, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) SIMD_AS_HWINTRINSIC_ID(Vector2, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) SIMD_AS_HWINTRINSIC_ID(Vector2, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -112,7 +55,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector2, op_Multiply, SIMD_AS_HWINTRINSIC_ID(Vector2, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector2, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* @@ -121,32 +63,16 @@ SIMD_AS_HWINTRINSIC_ID(Vector2, WithElement, // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector3 Intrinsics SIMD_AS_HWINTRINSIC_ID(Vector3, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector3, Create, ".ctor", 4, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector3, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector3, CreateFromVector2, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateFromVector2, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector3, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_Item, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitZ, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitZ, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector3, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) SIMD_AS_HWINTRINSIC_ID(Vector3, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) SIMD_AS_HWINTRINSIC_ID(Vector3, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -155,77 +81,15 @@ SIMD_AS_HWINTRINSIC_ID(Vector3, op_Multiply, SIMD_AS_HWINTRINSIC_ID(Vector3, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector3, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Vector4 Intrinsics -SIMD_AS_HWINTRINSIC_ID(Vector4, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Vector4, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateFromVector2, ".ctor", 4, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateFromVector2, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector4, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_Item, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitZ, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitZ, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitW, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitW, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector4, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector4, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) -SIMD_AS_HWINTRINSIC_ID(Vector4, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Vector4, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // ISA ID Name NumArg Instructions Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector Intrinsics SIMD_AS_HWINTRINSIC_ID(VectorT, Abs, 1, {NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Add, 2, {NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, AndNot, 2, {NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, As, 1, {NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorByte, 1, {NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorDouble, 1, {NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt16, 1, {NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt32, 1, {NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt64, 1, {NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorNInt, 1, {NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorNUInt, 1, {NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorSByte, 1, {NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorSingle, 1, {NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt16, 1, {NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt32, 1, {NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt64, 1, {NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, BitwiseAnd, 2, {NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, BitwiseOr, 2, {NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Ceiling, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Ceiling, NI_VectorT_Ceiling}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ConditionalSelect, 3, {NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToDouble, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_ConvertToDouble, NI_VectorT_ConvertToDouble, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -240,18 +104,14 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToUInt64, SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToUInt64Native, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_ConvertToUInt64Native}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT, CreateBroadcast, ".ctor", 2, {NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, CreateSequence, 2, {NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(VectorT, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Divide, NI_VectorT_Divide}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Dot, 2, {NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_Illegal, NI_Illegal, NI_VectorT_Dot, NI_VectorT_Dot}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Equals, 2, {NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, EqualsAll, 2, {NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, EqualsAny, 2, {NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Floor, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Floor, NI_VectorT_Floor}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_FusedMultiplyAdd, NI_VectorT_FusedMultiplyAdd}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) SIMD_AS_HWINTRINSIC_ID(VectorT, get_AllBitsSet, 0, {NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, get_Indices, 0, {NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, get_Item, 2, {NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) SIMD_AS_HWINTRINSIC_ID(VectorT, get_One, 0, {NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, get_Zero, 0, {NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GetElement, 2, {NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GreaterThan, 2, {NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GreaterThanAll, 2, {NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll}, SimdAsHWIntrinsicFlag::None) @@ -265,25 +125,20 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanAny, SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqual, 2, {NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqualAll, 2, {NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqualAny, 2, {NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Load, 1, {NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadAligned, 1, {NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadAlignedNonTemporal, 1, {NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadUnsafe, 1, {NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_NM(VectorT, LoadUnsafeIndex, "LoadUnsafe", 2, {NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, Max, 2, {NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Min, 2, {NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Multiply, 2, {NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_Illegal, NI_Illegal, NI_VectorT_Multiply, NI_VectorT_Multiply}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_MultiplyAddEstimate, NI_VectorT_MultiplyAddEstimate}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Narrow, 2, {NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) -SIMD_AS_HWINTRINSIC_ID(VectorT, Negate, 1, {NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, OnesComplement, 2, {NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Addition, 2, {NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_BitwiseAnd, 2, {NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_BitwiseOr, 2, {NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_op_Division, NI_VectorT_op_Division}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Equality, 2, {NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_ExclusiveOr, 2, {NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, op_Explicit, 1, {NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Inequality, 2, {NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_LeftShift, 2, {NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Multiply, 2, {NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_Illegal, NI_Illegal, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply}, SimdAsHWIntrinsicFlag::None) @@ -291,24 +146,17 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, op_OnesComplement, SIMD_AS_HWINTRINSIC_ID(VectorT, op_RightShift, 2, {NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Subtraction, 2, {NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnaryNegation, 1, {NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnaryPlus, 1, {NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnsignedRightShift, 2, {NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftLeft, 2, {NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftRightArithmetic, 2, {NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftRightLogical, 2, {NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Sqrt, NI_VectorT_Sqrt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Store, 2, {NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreAligned, 2, {NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreAlignedNonTemporal, 2, {NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreUnsafe, 2, {NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(VectorT, StoreUnsafeIndex, "StoreUnsafe", 3, {NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(VectorT, Subtract, 2, {NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Sum, 1, {NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ToScalar, 1, {NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WidenLower, 1, {NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WidenUpper, 1, {NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WithElement, 3, {NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Xor, 2, {NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor}, SimdAsHWIntrinsicFlag::None) #undef SIMD_AS_HWINTRINSIC_NM #undef SIMD_AS_HWINTRINSIC_ID diff --git a/src/coreclr/jit/simdashwintrinsiclistxarch.h b/src/coreclr/jit/simdashwintrinsiclistxarch.h index c0890478b8066..f119a4e051d59 100644 --- a/src/coreclr/jit/simdashwintrinsiclistxarch.h +++ b/src/coreclr/jit/simdashwintrinsiclistxarch.h @@ -32,78 +32,21 @@ * Each intrinsic has one or more flags with type of `enum SimdAsHWIntrinsicFlag` */ -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Plane Intrinsics -SIMD_AS_HWINTRINSIC_NM(Plane, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Plane, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_NM(Plane, CreateFromVector4, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_CreateFromVector4, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Plane, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Plane, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Plane, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Plane_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Quaternion Intrinsics -SIMD_AS_HWINTRINSIC_ID(Quaternion, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Conjugate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Conjugate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Quaternion, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Quaternion, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Identity, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Identity, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Item, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Quaternion, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Inverse, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Inverse, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Quaternion, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Quaternion, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Quaternion_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // ISA ID Name NumArg Instructions Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector2 Intrinsics SIMD_AS_HWINTRINSIC_ID(Vector2, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector2, Create, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector2, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(Vector2, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_Item, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector2, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) SIMD_AS_HWINTRINSIC_ID(Vector2, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) SIMD_AS_HWINTRINSIC_ID(Vector2, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -112,7 +55,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector2, op_Multiply, SIMD_AS_HWINTRINSIC_ID(Vector2, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector2, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector2, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector2, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* @@ -121,32 +63,16 @@ SIMD_AS_HWINTRINSIC_ID(Vector2, WithElement, // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector3 Intrinsics SIMD_AS_HWINTRINSIC_ID(Vector3, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector3, Create, ".ctor", 4, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector3, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(Vector3, CreateFromVector2, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateFromVector2, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector3, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_Item, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_UnitZ, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_UnitZ, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector3, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) SIMD_AS_HWINTRINSIC_ID(Vector3, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) SIMD_AS_HWINTRINSIC_ID(Vector3, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -155,77 +81,15 @@ SIMD_AS_HWINTRINSIC_ID(Vector3, op_Multiply, SIMD_AS_HWINTRINSIC_ID(Vector3, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(Vector3, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector3, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(Vector3, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// ISA ID Name NumArg Instructions Flags -// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} -// ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* -// Vector4 Intrinsics -SIMD_AS_HWINTRINSIC_ID(Vector4, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Add, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Add, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Clamp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Clamp, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Vector4, Create, ".ctor", 5, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Create, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateFromVector2, ".ctor", 4, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateFromVector2, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_NM(Vector4, CreateFromVector3, ".ctor", 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateFromVector3, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector4, Distance, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Distance, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, DistanceSquared, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_DistanceSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Divide, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_FusedMultiplyAdd, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_Item, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_Item, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitX, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitX, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitY, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitY, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitZ, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitZ, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_UnitW, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_UnitW, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, GetElement, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_GetElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Length, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Length, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector4, LengthSquared, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_LengthSquared, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod) -SIMD_AS_HWINTRINSIC_ID(Vector4, Lerp, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Lerp, NI_Illegal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1 | SimdAsHWIntrinsicFlag::SpillSideEffectsOp2) -SIMD_AS_HWINTRINSIC_ID(Vector4, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Min, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Min, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_MultiplyAddEstimate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Negate, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Negate, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Normalize, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Normalize, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Addition, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Addition, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Division, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Equality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Inequality, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Inequality, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Multiply, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_Subtraction, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_Subtraction, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, op_UnaryNegation, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_op_UnaryNegation, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_NM(Vector4, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Sqrt, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, Subtract, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Subtract, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(Vector4, WithElement, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_WithElement, NI_Illegal}, SimdAsHWIntrinsicFlag::None) - // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // ISA ID Name NumArg Instructions Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************* // Vector Intrinsics SIMD_AS_HWINTRINSIC_ID(VectorT, Abs, 1, {NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs, NI_VectorT_Abs}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Add, 2, {NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add, NI_VectorT_Add}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, AndNot, 2, {NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot, NI_VectorT_AndNot}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(VectorT, As, 1, {NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As, NI_VectorT_As}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorByte, 1, {NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte, NI_VectorT_AsVectorByte}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorDouble, 1, {NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble, NI_VectorT_AsVectorDouble}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt16, 1, {NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16, NI_VectorT_AsVectorInt16}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt32, 1, {NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32, NI_VectorT_AsVectorInt32}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorInt64, 1, {NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64, NI_VectorT_AsVectorInt64}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorNInt, 1, {NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt, NI_VectorT_AsVectorNInt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorNUInt, 1, {NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt, NI_VectorT_AsVectorNUInt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorSByte, 1, {NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte, NI_VectorT_AsVectorSByte}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorSingle, 1, {NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle, NI_VectorT_AsVectorSingle}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt16, 1, {NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16, NI_VectorT_AsVectorUInt16}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt32, 1, {NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32, NI_VectorT_AsVectorUInt32}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, AsVectorUInt64, 1, {NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64, NI_VectorT_AsVectorUInt64}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, BitwiseAnd, 2, {NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd, NI_VectorT_BitwiseAnd}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, BitwiseOr, 2, {NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr, NI_VectorT_BitwiseOr}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Ceiling, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Ceiling, NI_VectorT_Ceiling}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ConditionalSelect, 3, {NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect, NI_VectorT_ConditionalSelect}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToDouble, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_ConvertToDouble, NI_VectorT_ConvertToDouble, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) @@ -240,18 +104,14 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToUInt64, SIMD_AS_HWINTRINSIC_ID(VectorT, ConvertToUInt64Native, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_ConvertToUInt64Native}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT, CreateBroadcast, ".ctor", 2, {NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast, NI_VectorT_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, CreateSequence, 2, {NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence, NI_VectorT_CreateSequence}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(VectorT, Divide, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Divide, NI_VectorT_Divide}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Dot, 2, {NI_Illegal, NI_Illegal, NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_VectorT_Dot, NI_Illegal, NI_Illegal, NI_VectorT_Dot, NI_VectorT_Dot}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Equals, 2, {NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals, NI_VectorT_Equals}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, EqualsAll, 2, {NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll, NI_VectorT_EqualsAll}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, EqualsAny, 2, {NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny, NI_VectorT_EqualsAny}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Floor, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Floor, NI_VectorT_Floor}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, FusedMultiplyAdd, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_FusedMultiplyAdd, NI_VectorT_FusedMultiplyAdd}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, get_AllBitsSet, 0, {NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet, NI_VectorT_get_AllBitsSet}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, get_Indices, 0, {NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices, NI_VectorT_get_Indices}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, get_Item, 2, {NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item, NI_VectorT_get_Item}, SimdAsHWIntrinsicFlag::InstanceMethod | SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) SIMD_AS_HWINTRINSIC_ID(VectorT, get_One, 0, {NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One, NI_VectorT_get_One}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, get_Zero, 0, {NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero, NI_VectorT_get_Zero}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GetElement, 2, {NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement, NI_VectorT_GetElement}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GreaterThan, 2, {NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan, NI_VectorT_GreaterThan}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, GreaterThanAll, 2, {NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll, NI_VectorT_GreaterThanAll}, SimdAsHWIntrinsicFlag::None) @@ -265,25 +125,20 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanAny, SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqual, 2, {NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual, NI_VectorT_LessThanOrEqual}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqualAll, 2, {NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll, NI_VectorT_LessThanOrEqualAll}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, LessThanOrEqualAny, 2, {NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny, NI_VectorT_LessThanOrEqualAny}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Load, 1, {NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load, NI_VectorT_Load}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadAligned, 1, {NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned, NI_VectorT_LoadAligned}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadAlignedNonTemporal, 1, {NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal, NI_VectorT_LoadAlignedNonTemporal}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, LoadUnsafe, 1, {NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe, NI_VectorT_LoadUnsafe}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_NM(VectorT, LoadUnsafeIndex, "LoadUnsafe", 2, {NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex, NI_VectorT_LoadUnsafeIndex}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) SIMD_AS_HWINTRINSIC_ID(VectorT, Max, 2, {NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max, NI_VectorT_Max}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Min, 2, {NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min, NI_VectorT_Min}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Multiply, 2, {NI_Illegal, NI_Illegal, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply, NI_VectorT_Multiply}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, MultiplyAddEstimate, 3, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_MultiplyAddEstimate, NI_VectorT_MultiplyAddEstimate}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Narrow, 2, {NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow, NI_VectorT_Narrow}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) -SIMD_AS_HWINTRINSIC_ID(VectorT, Negate, 1, {NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate, NI_VectorT_Negate}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, OnesComplement, 1, {NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement, NI_VectorT_OnesComplement}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Addition, 2, {NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition, NI_VectorT_op_Addition}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_BitwiseAnd, 2, {NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd, NI_VectorT_op_BitwiseAnd}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_BitwiseOr, 2, {NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr, NI_VectorT_op_BitwiseOr}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Division, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_op_Division, NI_VectorT_op_Division}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Equality, 2, {NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality, NI_VectorT_op_Equality}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_ExclusiveOr, 2, {NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr, NI_VectorT_op_ExclusiveOr}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, op_Explicit, 1, {NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit, NI_VectorT_op_Explicit}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Inequality, 2, {NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality, NI_VectorT_op_Inequality}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_LeftShift, 2, {NI_Illegal, NI_Illegal, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift, NI_VectorT_op_LeftShift}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply, NI_Illegal, NI_Illegal, NI_VectorT_op_Multiply, NI_VectorT_op_Multiply}, SimdAsHWIntrinsicFlag::None) @@ -291,24 +146,17 @@ SIMD_AS_HWINTRINSIC_ID(VectorT, op_OnesComplement, SIMD_AS_HWINTRINSIC_ID(VectorT, op_RightShift, 2, {NI_Illegal, NI_Illegal, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift, NI_VectorT_op_RightShift}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_Subtraction, 2, {NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction, NI_VectorT_op_Subtraction}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnaryNegation, 1, {NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation, NI_VectorT_op_UnaryNegation}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnaryPlus, 1, {NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus, NI_VectorT_op_UnaryPlus}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, op_UnsignedRightShift, 2, {NI_Illegal, NI_Illegal, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift, NI_VectorT_op_UnsignedRightShift}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftLeft, 2, {NI_Illegal, NI_Illegal, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_VectorT_ShiftLeft, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftRightArithmetic, 2, {NI_Illegal, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_VectorT_ShiftRightArithmetic, NI_Illegal, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, ShiftRightLogical, 2, {NI_Illegal, NI_Illegal, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_VectorT_ShiftRightLogical, NI_Illegal, NI_Illegal}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_NM(VectorT, Sqrt, "SquareRoot", 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT_Sqrt, NI_VectorT_Sqrt}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Store, 2, {NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store, NI_VectorT_Store}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreAligned, 2, {NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned, NI_VectorT_StoreAligned}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreAlignedNonTemporal, 2, {NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal, NI_VectorT_StoreAlignedNonTemporal}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_ID(VectorT, StoreUnsafe, -1, {NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe, NI_VectorT_StoreUnsafe}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) SIMD_AS_HWINTRINSIC_NM(VectorT, StoreUnsafeIndex, "StoreUnsafe", 3, {NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex, NI_VectorT_StoreUnsafeIndex}, SimdAsHWIntrinsicFlag::SpillSideEffectsOp1) -SIMD_AS_HWINTRINSIC_ID(VectorT, Subtract, 2, {NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract, NI_VectorT_Subtract}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, Sum, 1, {NI_Illegal, NI_Illegal, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_VectorT_Sum, NI_Illegal, NI_Illegal, NI_VectorT_Sum, NI_VectorT_Sum}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, ToScalar, 1, {NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar, NI_VectorT_ToScalar}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WidenLower, 1, {NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower, NI_VectorT_WidenLower}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WidenUpper, 1, {NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper, NI_VectorT_WidenUpper}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT, WithElement, 3, {NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement, NI_VectorT_WithElement}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT, Xor, 2, {NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor, NI_VectorT_Xor}, SimdAsHWIntrinsicFlag::None) #undef SIMD_AS_HWINTRINSIC_NM #undef SIMD_AS_HWINTRINSIC_ID diff --git a/src/coreclr/jit/ssabuilder.cpp b/src/coreclr/jit/ssabuilder.cpp index 0a5229e32b8b7..a0b018b3078b4 100644 --- a/src/coreclr/jit/ssabuilder.cpp +++ b/src/coreclr/jit/ssabuilder.cpp @@ -1198,6 +1198,7 @@ void SsaBuilder::RenameVariables() assert(varDsc->lvTracked); if (varDsc->lvIsParam || m_pCompiler->info.compInitMem || varDsc->lvMustInit || + (varTypeIsGC(varDsc) && !varDsc->lvHasExplicitInit) || VarSetOps::IsMember(m_pCompiler, m_pCompiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex)) { unsigned ssaNum = varDsc->lvPerSsaData.AllocSsaNum(m_allocator); diff --git a/src/coreclr/jit/target.h b/src/coreclr/jit/target.h index 7e144eb9c5b21..64a0a6864bbb1 100644 --- a/src/coreclr/jit/target.h +++ b/src/coreclr/jit/target.h @@ -229,15 +229,61 @@ typedef uint64_t regMaskSmall; #define REG_MASK_ALL_FMT "%016llX" #endif -typedef regMaskSmall SingleTypeRegSet; +#ifdef TARGET_ARM64 +#define HAS_MORE_THAN_64_REGISTERS 1 +#endif // TARGET_ARM64 + +#ifdef HAS_MORE_THAN_64_REGISTERS +#define MORE_THAN_64_REGISTERS_ARG(x) , x +#else +#define MORE_THAN_64_REGISTERS_ARG(x) +#endif + +// TODO: Rename regMaskSmall as RegSet64 (at least for 64-bit) +typedef regMaskSmall SingleTypeRegSet; +inline SingleTypeRegSet genSingleTypeRegMask(regNumber reg); struct regMaskTP { private: + // Represents combined registers bitset including gpr/float and on some platforms + // mask or predicate registers regMaskSmall low; +#ifdef HAS_MORE_THAN_64_REGISTERS + regMaskSmall high; +#endif + public: - constexpr regMaskTP(regMaskSmall regMask) + +#ifdef TARGET_ARM + void AddRegNumInMask(regNumber reg, var_types type); + void RemoveRegNumFromMask(regNumber reg, var_types type); + bool IsRegNumInMask(regNumber reg, var_types type) const; +#endif + void AddGprRegs(SingleTypeRegSet gprRegs); + void AddRegNum(regNumber reg, var_types type); + void AddRegNumInMask(regNumber reg); + void AddRegsetForType(SingleTypeRegSet regsToAdd, var_types type); + SingleTypeRegSet GetRegSetForType(var_types type) const; + bool IsRegNumInMask(regNumber reg) const; + bool IsRegNumPresent(regNumber reg, var_types type) const; + void RemoveRegNum(regNumber reg, var_types type); + void RemoveRegNumFromMask(regNumber reg); + void RemoveRegsetForType(SingleTypeRegSet regsToRemove, var_types type); + + regMaskTP(regMaskSmall lowMask, regMaskSmall highMask) + : low(lowMask) +#ifdef HAS_MORE_THAN_64_REGISTERS + , high(highMask) +#endif + { + } + + regMaskTP(regMaskSmall regMask) : low(regMask) +#ifdef HAS_MORE_THAN_64_REGISTERS + , high(RBM_NONE) +#endif { } @@ -247,11 +293,18 @@ struct regMaskTP explicit operator bool() const { +#ifdef HAS_MORE_THAN_64_REGISTERS + return (low | high) != RBM_NONE; +#else return low != RBM_NONE; +#endif } explicit operator regMaskSmall() const { +#ifdef HAS_MORE_THAN_64_REGISTERS + assert(high == RBM_NONE); +#endif return (regMaskSmall)low; } @@ -278,68 +331,99 @@ struct regMaskTP return low; } - bool IsEmpty() +#ifdef HAS_MORE_THAN_64_REGISTERS + regMaskSmall getHigh() const { + return high; + } +#endif + + bool IsEmpty() const + { +#ifdef HAS_MORE_THAN_64_REGISTERS + return (low | high) == RBM_NONE; +#else return low == RBM_NONE; +#endif } - bool IsNonEmpty() + bool IsNonEmpty() const { return !IsEmpty(); } - SingleTypeRegSet GetRegSetForType(var_types type) const + SingleTypeRegSet GetIntRegSet() const + { + return getLow(); + } + + SingleTypeRegSet GetFloatRegSet() const { return getLow(); } - void RemoveRegNumFromMask(regNumber reg); + SingleTypeRegSet GetPredicateRegSet() const + { +#ifdef HAS_MORE_THAN_64_REGISTERS + return getHigh(); +#else + return getLow(); +#endif + } - bool IsRegNumInMask(regNumber reg); + void operator|=(const regMaskTP& second) + { + low |= second.getLow(); +#ifdef HAS_MORE_THAN_64_REGISTERS + high |= second.getHigh(); +#endif + } + + void operator^=(const regMaskTP& second) + { + low ^= second.getLow(); +#ifdef HAS_MORE_THAN_64_REGISTERS + high ^= second.getHigh(); +#endif + } + + void operator&=(const regMaskTP& second) + { + low &= second.getLow(); +#ifdef HAS_MORE_THAN_64_REGISTERS + high &= second.getHigh(); +#endif + } }; -static regMaskTP operator^(regMaskTP first, regMaskTP second) +static regMaskTP operator^(const regMaskTP& first, const regMaskTP& second) { - regMaskTP result(first.getLow() ^ second.getLow()); + regMaskTP result(first.getLow() ^ second.getLow() MORE_THAN_64_REGISTERS_ARG(first.getHigh() ^ second.getHigh())); return result; } -static regMaskTP operator&(regMaskTP first, regMaskTP second) +static regMaskTP operator&(const regMaskTP& first, const regMaskTP& second) { - regMaskTP result(first.getLow() & second.getLow()); + regMaskTP result(first.getLow() & second.getLow() MORE_THAN_64_REGISTERS_ARG(first.getHigh() & second.getHigh())); return result; } -static regMaskTP operator|(regMaskTP first, regMaskTP second) +static regMaskTP operator|(const regMaskTP& first, const regMaskTP& second) { - regMaskTP result(first.getLow() | second.getLow()); + regMaskTP result(first.getLow() | second.getLow() MORE_THAN_64_REGISTERS_ARG(first.getHigh() | second.getHigh())); return result; } -static regMaskTP& operator|=(regMaskTP& first, regMaskTP second) +static bool operator==(const regMaskTP& first, const regMaskTP& second) { - first = first | second; - return first; -} - -static regMaskTP& operator^=(regMaskTP& first, regMaskTP second) -{ - first = first ^ second; - return first; -} - -static regMaskTP& operator&=(regMaskTP& first, regMaskTP second) -{ - first = first & second; - return first; -} - -static bool operator==(regMaskTP first, regMaskTP second) -{ - return (first.getLow() == second.getLow()); + return (first.getLow() == second.getLow()) +#ifdef HAS_MORE_THAN_64_REGISTERS + && (first.getHigh() == second.getHigh()) +#endif + ; } -static bool operator!=(regMaskTP first, regMaskTP second) +static bool operator!=(const regMaskTP& first, const regMaskTP& second) { return !(first == second); } @@ -356,39 +440,70 @@ static bool operator>(regMaskTP first, regMaskTP second) return first.getLow() > second.getLow(); } -static regMaskTP operator<<(regMaskTP& first, const int b) +static regMaskTP operator<<(regMaskTP first, const int b) { regMaskTP result(first.getLow() << b); return result; } -static regMaskTP operator>>(regMaskTP& first, const int b) +static regMaskTP& operator<<=(regMaskTP& first, const int b) +{ + first = first << b; + return first; +} +#endif + +static regMaskTP operator>>(regMaskTP first, const int b) { regMaskTP result(first.getLow() >> b); return result; } -static regMaskTP& operator<<=(regMaskTP& first, const int b) +static regMaskTP& operator>>=(regMaskTP& first, const int b) { - first = first << b; + first = first >> b; return first; } -#endif -static regMaskTP operator~(regMaskTP first) +static regMaskTP operator~(const regMaskTP& first) { - regMaskTP result(~first.getLow()); + regMaskTP result(~first.getLow() MORE_THAN_64_REGISTERS_ARG(~first.getHigh())); return result; } -static uint32_t PopCount(regMaskTP value) +static uint32_t PopCount(SingleTypeRegSet value) { - return BitOperations::PopCount(value.getLow()); + return BitOperations::PopCount(value); } -static uint32_t BitScanForward(regMaskTP mask) +static uint32_t PopCount(const regMaskTP& value) { + return BitOperations::PopCount(value.getLow()) +#ifdef HAS_MORE_THAN_64_REGISTERS + + BitOperations::PopCount(value.getHigh()) +#endif + ; +} + +static uint32_t BitScanForward(SingleTypeRegSet value) +{ + return BitOperations::BitScanForward(value); +} + +static uint32_t BitScanForward(const regMaskTP& mask) +{ +#ifdef HAS_MORE_THAN_64_REGISTERS + if (mask.getLow() != RBM_NONE) + { + return BitOperations::BitScanForward(mask.getLow()); + } + else + { + return 64 + BitOperations::BitScanForward(mask.getHigh()); + } +#else return BitOperations::BitScanForward(mask.getLow()); +#endif } /*****************************************************************************/ @@ -508,8 +623,8 @@ inline bool isByteReg(regNumber reg) } #endif -inline SingleTypeRegSet genRegMask(regNumber reg); -inline SingleTypeRegSet genRegMaskFloat(regNumber reg ARM_ARG(var_types type = TYP_DOUBLE)); +inline regMaskTP genRegMask(regNumber reg); +inline regMaskTP genRegMaskFloat(regNumber reg ARM_ARG(var_types type = TYP_DOUBLE)); /***************************************************************************** * Return true if the register number is valid @@ -673,7 +788,7 @@ inline SingleTypeRegSet fullIntArgRegMask(CorInfoCallConvExtension callConv) // inline bool isValidIntArgReg(regNumber reg, CorInfoCallConvExtension callConv) { - return (genRegMask(reg) & fullIntArgRegMask(callConv)) != 0; + return (genSingleTypeRegMask(reg) & fullIntArgRegMask(callConv)) != 0; } //------------------------------------------------------------------------------------------- @@ -732,14 +847,47 @@ inline bool floatRegCanHoldType(regNumber reg, var_types type) } #endif +extern const regMaskSmall regMasks[REG_COUNT]; + /***************************************************************************** * - * Map a register number to a register mask. + * Map a register number to a floating-point register mask. */ +inline SingleTypeRegSet genSingleTypeFloatMask(regNumber reg ARM_ARG(var_types type /* = TYP_DOUBLE */)) +{ +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_LOONGARCH64) || \ + defined(TARGET_RISCV64) + assert(genIsValidFloatReg(reg)); + assert((unsigned)reg < ArrLen(regMasks)); + return regMasks[reg]; +#elif defined(TARGET_ARM) + assert(floatRegCanHoldType(reg, type)); + assert(reg >= REG_F0 && reg <= REG_F31); -extern const regMaskSmall regMasks[REG_COUNT]; + if (type == TYP_DOUBLE) + { + return regMasks[reg] | regMasks[reg + 1]; + } + else + { + return regMasks[reg]; + } +#else +#error Unsupported or unset target architecture +#endif +} -inline SingleTypeRegSet genRegMask(regNumber reg) +//------------------------------------------------------------------------ +// genSingleTypeRegMask: Given a register, generate the appropriate regMask +// +// Arguments: +// regNum - the register of interest +// +// Return Value: +// This will usually return the same value as genRegMask(regNum), except +// that it will return a 64-bits (or 32-bits) entity instead of `regMaskTP`. +// +inline SingleTypeRegSet genSingleTypeRegMask(regNumber reg) { assert((unsigned)reg < ArrLen(regMasks)); #ifdef TARGET_AMD64 @@ -755,35 +903,63 @@ inline SingleTypeRegSet genRegMask(regNumber reg) #endif } -/***************************************************************************** - * - * Map a register number to a floating-point register mask. - */ - -inline SingleTypeRegSet genRegMaskFloat(regNumber reg ARM_ARG(var_types type /* = TYP_DOUBLE */)) +//------------------------------------------------------------------------ +// genSingleTypeRegMask: Given a register, generate the appropriate regMask +// +// Arguments: +// regNum - the register of interest +// type - the type of regNum (i.e. the type it is being used as) +// +// Return Value: +// This will usually return the same value as genRegMask(regNum), except +// that it will return a 64-bits (or 32-bits) entity instead of `regMaskTP`. +// On architectures where multiple registers are used for certain types +// (e.g. TYP_DOUBLE on ARM), it will return a regMask that includes +// all the registers for that type. +// +inline SingleTypeRegSet genSingleTypeRegMask(regNumber regNum, var_types type) { -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_LOONGARCH64) || \ - defined(TARGET_RISCV64) - assert(genIsValidFloatReg(reg)); - assert((unsigned)reg < ArrLen(regMasks)); - return regMasks[reg]; -#elif defined(TARGET_ARM) - assert(floatRegCanHoldType(reg, type)); - assert(reg >= REG_F0 && reg <= REG_F31); +#if defined(TARGET_ARM) + SingleTypeRegSet regMask = RBM_NONE; - if (type == TYP_DOUBLE) + if (varTypeUsesIntReg(type)) { - return regMasks[reg] | regMasks[reg + 1]; + regMask = genSingleTypeRegMask(regNum); } else { - return regMasks[reg]; + assert(varTypeUsesFloatReg(type)); + regMask = genSingleTypeFloatMask(regNum, type); } + + return regMask; #else -#error Unsupported or unset target architecture + return genSingleTypeRegMask(regNum); #endif } +/***************************************************************************** + * + * Map a register number to a register mask. + */ + +inline regMaskTP genRegMask(regNumber reg) +{ + regMaskTP result = RBM_NONE; + result.AddRegNumInMask(reg); + return result; +} + +/***************************************************************************** + * + * Map a register number to a floating-point register mask. + */ + +inline regMaskTP genRegMaskFloat(regNumber reg ARM_ARG(var_types type /* = TYP_DOUBLE */)) +{ + return regMaskTP(genSingleTypeFloatMask(reg ARM_ARG(type))); +} + //------------------------------------------------------------------------ // genRegMask: Given a register, and its type, generate the appropriate regMask // @@ -803,25 +979,36 @@ inline SingleTypeRegSet genRegMaskFloat(regNumber reg ARM_ARG(var_types type /* // For registers that are used in pairs, the caller will be handling // each member of the pair separately. // -inline SingleTypeRegSet genRegMask(regNumber regNum, var_types type) +inline regMaskTP genRegMask(regNumber regNum, var_types type) { -#if defined(TARGET_ARM) - SingleTypeRegSet regMask = RBM_NONE; + regMaskTP result = RBM_NONE; + result.AddRegNumInMask(regNum ARM_ARG(type)); + return result; +} - if (varTypeUsesIntReg(type)) +inline regNumber getRegForType(regNumber reg, var_types regType) +{ +#ifdef TARGET_ARM + if ((regType == TYP_DOUBLE) && !genIsValidDoubleReg(reg)) { - regMask = genRegMask(regNum); + reg = REG_PREV(reg); } - else +#endif // TARGET_ARM + return reg; +} + +inline SingleTypeRegSet getSingleTypeRegMask(regNumber reg, var_types regType) +{ + reg = getRegForType(reg, regType); + SingleTypeRegSet regMask = genSingleTypeRegMask(reg); +#ifdef TARGET_ARM + if (regType == TYP_DOUBLE) { - assert(varTypeUsesFloatReg(type)); - regMask = genRegMaskFloat(regNum, type); + assert(genIsValidDoubleReg(reg)); + regMask |= (regMask << 1); } - +#endif // TARGET_ARM return regMask; -#else - return genRegMask(regNum); -#endif } /***************************************************************************** diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index 97f0593901390..a51a52ab21d64 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -224,11 +224,12 @@ 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. + regBit >>= isFloat ? 2 : 1; regNum = isFloat ? REG_PREV(REG_PREV(regNum)) : REG_PREV(regNum); #else + regBit >>= 1; regNum = REG_PREV(regNum); #endif - regBit = genRegMask(regNum); } } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 909e5ab768af4..58a5285b2e350 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -10007,7 +10007,7 @@ PhaseStatus Compiler::fgValueNumber() ssaDef->m_vnPair.SetBoth(initVal); ssaDef->SetBlock(fgFirstBB); } - else if (info.compInitMem || varDsc->lvMustInit || + else if (info.compInitMem || varDsc->lvMustInit || (varTypeIsGC(varDsc) && !varDsc->lvHasExplicitInit) || VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, varDsc->lvVarIndex)) { // The last clause covers the use-before-def variables (the ones that are live-in to the first block), diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 5604cbafdf2b5..6a5032cd79ed7 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -165,7 +165,8 @@ // (Though some of these may be labeled "illegal"). enum VNFunc { - // Implicitly, elements of genTreeOps here. +#define GTNODE(en, st, cm, ivn, ok) VNF_##en, +#include "gtlist.h" VNF_Boundary = GT_COUNT, #define ValueNumFuncDef(nm, arity, commute, knownNonNull, sharedStatic, extra) VNF_##nm, #include "valuenumfuncs.h" diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs index 790d2f0fe219a..375d3de0994e5 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs @@ -5,14 +5,14 @@ namespace System.Runtime.InteropServices { /// /// Any method marked with can be directly called from - /// native code. The function token can be loaded to a local variable using the address-of operator + /// native code. The function token can be loaded to a local variable using the address-of operator /// in C# and passed as a callback to a native method. /// /// /// Methods marked with this attribute have the following restrictions: /// * Method must be marked "static". /// * Must not be called from managed code. - /// * Must only have blittable arguments. + /// * Must only have blittable arguments. /// [AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class UnmanagedCallersOnlyAttribute : Attribute diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index b0eb24759bb52..0bd9fbb34f9a5 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -152,7 +152,7 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim PTR_uint32_t xdata = dac_cast(pRuntimeFunction->UnwindData + moduleBase); int size = 4; - // See https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + // See https://learn.microsoft.com/cpp/build/arm64-exception-handling int unwindWords = xdata[0] >> 27; int epilogScopes = (xdata[0] >> 22) & 0x1f; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml index 69d5d5a83a40d..0e5e5abf78580 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 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 0fef8d453dac3..e3ee1f3eedb79 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 @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Runtime; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Internal.Runtime.Augments; @@ -179,12 +177,6 @@ public static IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisTyp return RuntimeImports.RhResolveDispatchOnType(thisType.ToMethodTable(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int hashCode4) { int length = 4; @@ -192,13 +184,13 @@ private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int int hash1 = 0x449b3ad6; int hash2 = (length << 3) + 0x55399219; - hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode1; - hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode2; - hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode3; - hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode4; + hash1 = (hash1 + int.RotateLeft(hash1, 5)) ^ hashCode1; + hash2 = (hash2 + int.RotateLeft(hash2, 5)) ^ hashCode2; + hash1 = (hash1 + int.RotateLeft(hash1, 5)) ^ hashCode3; + hash2 = (hash2 + int.RotateLeft(hash2, 5)) ^ hashCode4; - hash1 += _rotl(hash1, 8); - hash2 += _rotl(hash2, 8); + hash1 += int.RotateLeft(hash1, 8); + hash2 += int.RotateLeft(hash2, 8); return hash1 ^ hash2; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs index 10072b006c00e..12b0cde3b166f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs @@ -62,7 +62,7 @@ public sealed override string ToString() ComputeTypedArgumentString(namedArgument.TypedValue, typed)); } - return string.Format("[{0}({1}{2})]", AttributeType.FormatTypeNameForReflection(), ctorArgs, namedArgs); + return string.Format("[{0}({1}{2})]", AttributeType.FormatTypeName(), ctorArgs, namedArgs); } protected static ConstructorInfo ResolveAttributeConstructor( diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs index c76651833ae97..288456a6a42e1 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs @@ -114,7 +114,7 @@ public sealed override string ToString() if (parameters.Length == 0) throw new InvalidOperationException(); // Legacy: Why is a ToString() intentionally throwing an exception? RuntimeParameterInfo runtimeParameterInfo = (RuntimeParameterInfo)(parameters[0]); - return runtimeParameterInfo.ParameterType.FormatTypeNameForReflection() + " " + this.Name; + return runtimeParameterInfo.ParameterType.FormatTypeName() + " " + this.Name; } protected RuntimeEventInfo WithDebugName() diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs index fb70095f66ab0..1674cc9b18cfe 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs @@ -86,7 +86,7 @@ protected sealed override string MetadataName public sealed override string ToString() { - return FieldRuntimeType.ToType().FormatTypeNameForReflection() + " " + this.Name; + return FieldRuntimeType.ToType().FormatTypeName() + " " + this.Name; } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs index d4ded4d8bfa2e..21c07b740385d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs @@ -73,7 +73,7 @@ internal static string ComputeParametersString(RuntimeParameterInfo[] parameters { if (i != 0) sb.Append(", "); - string parameterTypeString = parameters[i].ParameterType.FormatTypeNameForReflection(); + string parameterTypeString = parameters[i].ParameterType.FormatTypeName(); // Legacy: Why use "ByRef" for by ref parameters? What language is this? // VB uses "ByRef" but it should precede (not follow) the parameter name. @@ -94,7 +94,7 @@ internal static string ComputeParametersString(RuntimeParameterInfo[] parameters internal static string ComputeToString(MethodBase contextMethod, RuntimeTypeInfo[] methodTypeArguments, RuntimeParameterInfo[] parameters, RuntimeParameterInfo returnParameter) { StringBuilder sb = new StringBuilder(30); - sb.Append(returnParameter == null ? "Void" : returnParameter.ParameterType.FormatTypeNameForReflection()); // ConstructorInfos allowed to pass in null rather than craft a ReturnParameterInfo that's always of type void. + sb.Append(returnParameter == null ? "Void" : returnParameter.ParameterType.FormatTypeName()); // ConstructorInfos allowed to pass in null rather than craft a ReturnParameterInfo that's always of type void. sb.Append(' '); sb.Append(contextMethod.Name); if (methodTypeArguments.Length != 0) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs index e42095d9db2b9..e74dbeb571ba4 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs @@ -72,7 +72,7 @@ public sealed override int Position public sealed override string ToString() { - return this.ParameterType.FormatTypeNameForReflection() + " " + this.Name; + return this.ParameterType.FormatTypeName() + " " + this.Name; } private readonly MemberInfo _member; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs index 55c832c5420e7..1665d354582f7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs @@ -214,7 +214,7 @@ public sealed override string ToString() { StringBuilder sb = new StringBuilder(30); - sb.Append(PropertyType.FormatTypeNameForReflection()); + sb.Append(PropertyType.FormatTypeName()); sb.Append(' '); sb.Append(this.Name); ParameterInfo[] indexParameters = this.GetIndexParameters(); 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 a2f5b48b498fb..916efe58017c7 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 @@ -181,13 +181,6 @@ public static unsafe bool TryEnsureSufficientExecutionStack() return (t_sufficientStackLimit = limit); } - [Intrinsic] - public static unsafe bool IsReferenceOrContainsReferences() - { - MethodTable* pEEType = MethodTable.Of(); - return !pEEType->IsValueType || pEEType->ContainsGCPointers; - } - [Intrinsic] internal static unsafe bool IsReference() { 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 e06ac7457049a..374ad0d8cd09c 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 @@ -131,13 +131,13 @@ internal enum CreateComInterfaceFlagsEx /// /// This is useful in scenarios when the caller has no need to rely on an IUnknown instance /// that is used when running managed code is not possible (i.e. during a GC). In traditional - /// COM scenarios this is common, but scenarios involving Reference Tracker hosting + /// COM scenarios this is common, but scenarios involving Reference Tracker hosting /// calling of the IUnknown API during a GC is possible. /// CallerDefinedIUnknown = 1, /// - /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. + /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown /// and as such none should be supplied by the caller. /// diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs index db507507496ce..01624ba5469dd 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; -using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -48,12 +46,6 @@ public bool Equals(RuntimeFieldHandle handle) return declaringType1.Equals(declaringType2) && fieldName1 == fieldName2; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - public override int GetHashCode() { if (_value == IntPtr.Zero) @@ -64,7 +56,7 @@ public override int GetHashCode() RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(this, out declaringType, out fieldName); int hashcode = declaringType.GetHashCode(); - return (hashcode + _rotl(hashcode, 13)) ^ fieldName.GetHashCode(); + return (hashcode + int.RotateLeft(hashcode, 13)) ^ fieldName.GetHashCode(); } public static RuntimeFieldHandle FromIntPtr(IntPtr value) => new RuntimeFieldHandle(value); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs index 139b61b3bbcdb..b79874529f651 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; -using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -68,12 +66,6 @@ public bool Equals(RuntimeMethodHandle handle) return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - public override int GetHashCode() { if (_value == IntPtr.Zero) @@ -85,13 +77,13 @@ public override int GetHashCode() RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(this, out declaringType, out nameAndSignature, out genericArgs); int hashcode = declaringType.GetHashCode(); - hashcode = (hashcode + _rotl(hashcode, 13)) ^ nameAndSignature.Name.GetHashCode(); + hashcode = (hashcode + int.RotateLeft(hashcode, 13)) ^ nameAndSignature.Name.GetHashCode(); if (genericArgs != null) { for (int i = 0; i < genericArgs.Length; i++) { int argumentHashCode = genericArgs[i].GetHashCode(); - hashcode = (hashcode + _rotl(hashcode, 13)) ^ argumentHashCode; + hashcode = (hashcode + int.RotateLeft(hashcode, 13)) ^ argumentHashCode; } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs index d5b3c54e1fc27..555572840055e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs @@ -56,35 +56,6 @@ private static unsafe RuntimeType GetTypeFromMethodTableSlow(MethodTable* pMT) } } - // - // This is a port of the desktop CLR's RuntimeType.FormatTypeName() routine. This routine is used by various Reflection ToString() methods - // to display the name of a type. Do not use for any other purpose as it inherits some pretty quirky desktop behavior. - // - internal string FormatTypeNameForReflection() - { - // Legacy: this doesn't make sense, why use only Name for nested types but otherwise - // ToString() which contains namespace. - Type rootElementType = this; - while (rootElementType.HasElementType) - rootElementType = rootElementType.GetElementType()!; - if (rootElementType.IsNested) - { - return Name!; - } - - // Legacy: why removing "System"? Is it just because C# has keywords for these types? - // If so why don't we change it to lower case to match the C# keyword casing? - string typeName = ToString(); - if (typeName.StartsWith("System.")) - { - if (rootElementType.IsPrimitive || rootElementType == typeof(void)) - { - typeName = typeName.Substring("System.".Length); - } - } - return typeName; - } - [Intrinsic] [RequiresUnreferencedCode("The type might be removed")] public static Type GetType(string typeName) => GetType(typeName, throwOnError: false, ignoreCase: false); diff --git a/src/coreclr/nativeaot/docs/compiling.md b/src/coreclr/nativeaot/docs/compiling.md index 757b00bb0b439..dcbe314646bce 100644 --- a/src/coreclr/nativeaot/docs/compiling.md +++ b/src/coreclr/nativeaot/docs/compiling.md @@ -1,6 +1,6 @@ # Compiling with Native AOT -Please consult [documentation](https://docs.microsoft.com/dotnet/core/deploying/native-aot) for instructions how to compile and publish application. +Please consult [documentation](https://learn.microsoft.com/dotnet/core/deploying/native-aot) for instructions how to compile and publish application. The rest of this document covers advanced topics only. diff --git a/src/coreclr/pgosupport.cmake b/src/coreclr/pgosupport.cmake index 3491bac7cf56c..99a7b358188a1 100644 --- a/src/coreclr/pgosupport.cmake +++ b/src/coreclr/pgosupport.cmake @@ -24,9 +24,6 @@ function(add_pgo TargetName) if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELEASE OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELWITHDEBINFO) target_compile_options(${TargetName} PRIVATE -flto -fprofile-instr-generate) set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS " -flto -fprofile-instr-generate") - if(NOT LD_LLVM) - set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS " -fuse-ld=gold") - endif() endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELEASE OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELWITHDEBINFO) endif(CLR_CMAKE_HOST_WIN32) elseif(CLR_CMAKE_PGO_OPTIMIZE AND NOT CLR_CMAKE_ENABLE_SANITIZERS) @@ -59,9 +56,6 @@ function(add_pgo TargetName) if(HAVE_LTO) target_compile_options(${TargetName} PRIVATE -flto -fprofile-instr-use=${ProfilePath} -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled) set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS " -flto -fprofile-instr-use=${ProfilePath}") - if(NOT LD_LLVM) - set_property(TARGET ${TargetName} APPEND_STRING PROPERTY LINK_FLAGS " -fuse-ld=gold") - endif() add_compile_definitions(WITH_NATIVE_PGO) else(HAVE_LTO) message(WARNING "LTO is not supported, skipping profile guided optimizations") diff --git a/src/coreclr/scripts/jitrollingbuild.py b/src/coreclr/scripts/jitrollingbuild.py index 07d48c1771210..93f1f02c28c58 100644 --- a/src/coreclr/scripts/jitrollingbuild.py +++ b/src/coreclr/scripts/jitrollingbuild.py @@ -462,7 +462,7 @@ def upload_blob(file, blob_name): except: logging.warning("Please install:") logging.warning(" pip install azure-storage-blob") - logging.warning("See also https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python") + logging.warning("See also https://learn.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-python") raise RuntimeError("Missing azure storage package.") blob_service_client = BlobServiceClient(account_url=az_blob_storage_account_uri, credential=coreclr_args.az_storage_key) diff --git a/src/coreclr/scripts/jitutil.py b/src/coreclr/scripts/jitutil.py index 7fad27c07a34f..78cb26e01c7fa 100644 --- a/src/coreclr/scripts/jitutil.py +++ b/src/coreclr/scripts/jitutil.py @@ -571,7 +571,7 @@ def require_azure_storage_libraries(need_azure_storage_blob=True, need_azure_ide logging.error(" pip install azure-storage-blob azure-identity") logging.error("or (Windows):") logging.error(" py -3 -m pip install azure-storage-blob azure-identity") - logging.error("See also https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python") + logging.error("See also https://learn.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-python") raise RuntimeError("Missing Azure Storage package.") # The Azure packages spam all kinds of output to the logging channels. @@ -585,7 +585,7 @@ def report_azure_error(): """ Report an Azure error """ logging.error("A problem occurred accessing Azure. Are you properly authenticated using the Azure CLI?") - logging.error("Install the Azure CLI from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli.") + logging.error("Install the Azure CLI from https://learn.microsoft.com/cli/azure/install-azure-cli.") logging.error("Then log in to Azure using `az login`.") diff --git a/src/coreclr/scripts/superpmi_collect_setup.py b/src/coreclr/scripts/superpmi_collect_setup.py index 54cd78f63e12d..34b2809a6d1f6 100644 --- a/src/coreclr/scripts/superpmi_collect_setup.py +++ b/src/coreclr/scripts/superpmi_collect_setup.py @@ -24,13 +24,9 @@ # 4. For benchmarks collections, a specialized script is called to set up the benchmarks collection. # 5. Lastly, it sets the pipeline variables. # -# Below are the helix queues and images it sets depending on the OS/architecture (accepted format by Helix is either "QueueName" or "(DisplayName)QueueName@Image") -# | Arch | windows | Linux | macOS | -# |-------|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------|----------------| -# | x86 | Windows.10.Amd64.X86.Rt | - | - | -# | x64 | Windows.10.Amd64.X86.Rt | Ubuntu.2204.Amd64 | OSX.1014.Amd64 | -# | arm | - | (Ubuntu.1804.Arm32)Ubuntu.2004.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm32v7 | - | -# | arm64 | Windows.11.Arm64 | (Ubuntu.1804.Arm64)Ubuntu.2004.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8 | OSX.1100.ARM64 | +# The helix queues used are defined below in the code, in the `helix_queue` variable. +# Note that this is unfortunate as it doesn't get updated when the common code in +# eng/pipelines/coreclr/templates/helix-queues-setup.yml is updated. # ################################################################################ ################################################################################ @@ -457,17 +453,18 @@ def main(main_args): ci = True # Determine the Helix queue name to use when running jobs. + # Note that we run in the 'internal', not 'public', instance, so we must use 'internal' queues. if platform_name == "windows": helix_queue = "Windows.11.Arm64" if arch == "arm64" else "Windows.10.Amd64.X86.Rt" elif platform_name == "linux": if arch == "arm": - helix_queue = "(Ubuntu.1804.Arm32)Ubuntu.2004.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm32v7" + helix_queue = "(Debian.12.Arm32)Ubuntu.2004.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-12-helix-arm32v7" elif arch == "arm64": helix_queue = "(Ubuntu.1804.Arm64)Ubuntu.2004.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8" else: helix_queue = "Ubuntu.2204.Amd64" elif platform_name == "osx": - helix_queue = "OSX.1100.ARM64" if arch == "arm64" else "OSX.1014.Amd64" + helix_queue = "OSX.1200.ARM64" if arch == "arm64" else "OSX.1200.Amd64" # Copy the superpmi scripts diff --git a/src/coreclr/scripts/superpmi_diffs_setup.py b/src/coreclr/scripts/superpmi_diffs_setup.py index c54de696ccd97..dd233a4191d84 100644 --- a/src/coreclr/scripts/superpmi_diffs_setup.py +++ b/src/coreclr/scripts/superpmi_diffs_setup.py @@ -168,7 +168,7 @@ def build_jit_analyze(coreclr_args, source_directory, jit_analyze_build_director # Build jit-analyze only, and build it as a self-contained app (not framework-dependent). # What target RID are we building? It depends on where we're going to run this code. - # The RID catalog is here: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog. + # The RID catalog is here: https://learn.microsoft.com/dotnet/core/rid-catalog. # Windows x64 => win-x64 # Windows x86 => win-x86 # Windows arm64 => win-arm64 diff --git a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs index 98d06568878dd..bef78e07ac7f0 100644 --- a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs +++ b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs @@ -81,12 +81,9 @@ private static class XArchIntrinsicConstants public const int Avx512Vbmi = 0x800000; public const int Avx512Vbmi_vl = 0x1000000; public const int Serialize = 0x2000000; - public const int VectorT128 = 0x4000000; - public const int VectorT256 = 0x8000000; - public const int VectorT512 = 0x10000000; - public const int Avx10v1 = 0x20000000; - public const int Avx10v1_v256 = 0x40000000; - public const int Avx10v1_v512 = unchecked((int)0x80000000); + public const int Avx10v1 = 0x4000000; + public const int Avx10v1_v256 = 0x8000000; + public const int Avx10v1_v512 = 0x10000000; public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags) { @@ -228,9 +225,9 @@ public static int FromInstructionSet(InstructionSet instructionSet) InstructionSet.X64_X86Base_X64 => 0, // Vector Sizes - InstructionSet.X64_VectorT128 => VectorT128, - InstructionSet.X64_VectorT256 => VectorT256, - InstructionSet.X64_VectorT512 => VectorT512, + InstructionSet.X64_VectorT128 => 0, + InstructionSet.X64_VectorT256 => Avx2, + InstructionSet.X64_VectorT512 => Avx512f, _ => throw new NotSupportedException(((InstructionSet_X64)instructionSet).ToString()) }; @@ -249,9 +246,8 @@ private static class Arm64IntrinsicConstants public const int Sha256 = 0x0040; public const int Atomics = 0x0080; public const int Rcpc = 0x0100; - public const int VectorT128 = 0x0200; - public const int Rcpc2 = 0x0400; - public const int Sve = 0x0800; + public const int Rcpc2 = 0x0200; + public const int Sve = 0x0400; public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags) { @@ -310,7 +306,7 @@ public static int FromInstructionSet(InstructionSet instructionSet) InstructionSet.ARM64_Sve_Arm64 => Sve, // Vector Sizes - InstructionSet.ARM64_VectorT128 => VectorT128, + InstructionSet.ARM64_VectorT128 => AdvSimd, _ => throw new NotSupportedException(((InstructionSet_ARM64)instructionSet).ToString()) }; diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs index 67b62fc84c508..0a347d7ab3c56 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Numerics; // Managed mirror of NativeFormatWriter.h/.cpp namespace Internal.NativeFormat @@ -2013,16 +2014,10 @@ public void Append(uint hashcode, Vertex element) _Entries.Add(new Entry(hashcode, element)); } - // Returns 1 + log2(x) rounded up, 0 iff x == 0 + // Calculates the highest bit set in a given unsigned integer. private static int HighestBit(uint x) { - int ret = 0; - while (x != 0) - { - x >>= 1; - ret++; - } - return ret; + return (sizeof(uint) * 8) - BitOperations.LeadingZeroCount(x); } // Helper method to back patch entry index in the bucket table diff --git a/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs index 35d3c877e0506..2f6d2c7f04d6f 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs @@ -193,15 +193,7 @@ internal static uint ComputeValueTypeFieldPaddingFieldValue(uint padding, uint a if ((padding == 0) && (alignment == targetPointerSize)) return 0; - uint alignmentLog2 = 0; - Debug.Assert(alignment != 0); - - while ((alignment & 1) == 0) - { - alignmentLog2++; - alignment >>= 1; - } - Debug.Assert(alignment == 1); + uint alignmentLog2 = uint.TrailingZeroCount(alignment); Debug.Assert(ValueTypePaddingMax >= padding); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 8adbb56aaa070..85d64b161f7cb 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -883,6 +883,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s ThrowHelper.ThrowBadImageFormatException(); if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS; + if (signature.IsExplicitThis) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_EXPLICITTHIS; TypeDesc returnType = signature.ReturnType; diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 1ff2ad218cbdd..ff85e36436176 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -61,17 +61,12 @@ public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defTyp // A type has layout if it is marked SequentialLayout or ExplicitLayout. If any type within an inheritance chain has layout, // then so shall all its base classes, up to the one that descends immediately from System.ValueType (if it exists in the type's // hierarchy); otherwise, from System.Object - // Note: While the CLI isn't clearly worded, the layout needs to be the same for the entire chain. // If the current type isn't ValueType or System.Object and has a layout and the parent type isn't - // ValueType or System.Object then the layout type attributes need to match - if ((!type.IsValueType && !type.IsObject) && - (type.IsSequentialLayout || type.IsExplicitLayout) && - (!type.BaseType.IsValueType && !type.BaseType.IsObject)) + // ValueType or System.Object then both need to have layout. + if (!type.IsValueType && type.HasLayout()) { MetadataType baseType = type.MetadataBaseType; - - if (type.IsSequentialLayout != baseType.IsSequentialLayout || - type.IsExplicitLayout != baseType.IsExplicitLayout) + if (!baseType.IsObject && !baseType.HasLayout()) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs index 43617899802a4..639455ff98ba2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs @@ -22,6 +22,7 @@ public enum MethodSignatureFlags UnmanagedCallingConvention = 0x0009, Static = 0x0010, + ExplicitThis = 0x0020, } public enum EmbeddedSignatureDataKind @@ -129,6 +130,14 @@ public bool IsStatic } } + public bool IsExplicitThis + { + get + { + return (_flags & MethodSignatureFlags.ExplicitThis) != 0; + } + } + public int GenericParameterCount { get diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs index be72b3d71accf..e68a042817092 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -380,6 +380,9 @@ private MethodSignature ParseMethodSignatureImpl(bool skipEmbeddedSignatureData) if (!header.IsInstance) flags |= MethodSignatureFlags.Static; + if (header.HasExplicitThis) + flags |= MethodSignatureFlags.ExplicitThis; + int arity = header.IsGeneric ? _reader.ReadCompressedInteger() : 0; int count = _reader.ReadCompressedInteger(); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs index a1cf526bef2e3..c0318b02959dc 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs @@ -43,11 +43,7 @@ public static MethodIL EmitIL(MethodDesc method) return null; bool result; - if (methodName == "IsReferenceOrContainsReferences") - { - result = elementType.IsGCPointer || (elementType is DefType defType && defType.ContainsGCPointers); - } - else if (methodName == "IsReference") + if (methodName == "IsReference") { result = elementType.IsGCPointer; } diff --git a/src/coreclr/tools/Common/TypeSystem/MetadataEmitter/TypeSystemMetadataEmitter.cs b/src/coreclr/tools/Common/TypeSystem/MetadataEmitter/TypeSystemMetadataEmitter.cs index 76582ef0e29ab..77d31895e7b13 100644 --- a/src/coreclr/tools/Common/TypeSystem/MetadataEmitter/TypeSystemMetadataEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/MetadataEmitter/TypeSystemMetadataEmitter.cs @@ -658,9 +658,9 @@ public void Pop() private void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig, EmbeddedSignatureDataEmitter signatureDataEmitter) { signatureDataEmitter.Push(); - BlobEncoder signatureEncoder = new BlobEncoder(signatureBuilder); int genericParameterCount = sig.GenericParameterCount; bool isInstanceMethod = !sig.IsStatic; + bool isExplicitThis = sig.IsExplicitThis; SignatureCallingConvention sigCallingConvention = SignatureCallingConvention.Default; switch (sig.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) { @@ -685,7 +685,15 @@ private void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature if (sigCallingConvention != SignatureCallingConvention.Default) signatureDataEmitter.UpdateSignatureCallingConventionAtCurrentIndexStack(ref sigCallingConvention); - signatureEncoder.MethodSignature(sigCallingConvention, genericParameterCount, isInstanceMethod); + SignatureAttributes attributes = + (genericParameterCount != 0 ? SignatureAttributes.Generic : 0) | + (isInstanceMethod ? SignatureAttributes.Instance : 0) | + (isExplicitThis ? SignatureAttributes.ExplicitThis : 0); + + signatureBuilder.WriteByte(new SignatureHeader(SignatureKind.Method, sigCallingConvention, attributes).RawValue); + if (genericParameterCount != 0) + signatureBuilder.WriteCompressedInteger(genericParameterCount); + signatureBuilder.WriteCompressedInteger(sig.Length); EncodeType(signatureBuilder, sig.ReturnType, signatureDataEmitter); for (int i = 0; i < sig.Length; i++) diff --git a/src/coreclr/tools/GCLogParser/App.config b/src/coreclr/tools/GCLogParser/App.config deleted file mode 100644 index c8f375802e873..0000000000000 --- a/src/coreclr/tools/GCLogParser/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/coreclr/tools/GCLogParser/Properties/AssemblyInfo.cs b/src/coreclr/tools/GCLogParser/Properties/AssemblyInfo.cs deleted file mode 100644 index b08b750df856e..0000000000000 --- a/src/coreclr/tools/GCLogParser/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("parse-hb-log")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("parse-hb-log")] -[assembly: AssemblyCopyright("Copyright \u00A9 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6ae26cd6-c971-48d6-8c03-2ffa272b942c")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/coreclr/tools/GCLogParser/parse-hb-log.cs b/src/coreclr/tools/GCLogParser/parse-hb-log.cs index 2f0b5888f1c1e..ebf1a01a32245 100644 --- a/src/coreclr/tools/GCLogParser/parse-hb-log.cs +++ b/src/coreclr/tools/GCLogParser/parse-hb-log.cs @@ -1,4 +1,7 @@ -// parse-hb-log.exe -ia 1 -l gclog.pid.log +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// parse-hb-log.exe -ia 1 -l gclog.pid.log // where gclog.pid.log is the log you get from GC with HEAP_BALANCE_LOG. See // comments for it in gcpriv.h // @@ -36,19 +39,20 @@ using System; using System.Collections.Generic; -using System.Text; using System.IO; using System.Linq; +using System.Text; namespace parse_hb_log { - enum HeapBalanceFlagMask + file enum HeapBalanceFlagMask { MutipleProcs = 0xf, EnterDueToProc = 0xf0, SetIdeal = 0xf00, }; - struct SampleInfo + + file struct SampleInfo { // -1 means uninit. public int tid; @@ -70,17 +74,18 @@ public SampleInfo(int _tid, int _allocHeap, int _countSamples, int _flags, int _ } }; - enum PassOneViewType + file enum PassOneViewType { Thread = 0, AllocHeap = 1, MaxType = 2 }; - class Program + + file sealed class Program { - static bool fLogging = false; + private static bool fLogging = false; - static string ParseString(string str, string strStart, string strEnd, out string strRemaining) + private static string ParseString(string str, string strStart, string strEnd, out string strRemaining) { int startIndex = 0; if (strStart != null) @@ -98,7 +103,7 @@ static string ParseString(string str, string strStart, string strEnd, out string //string strRest = str.Substring(startIndex + strStart.Length); string strRest = str.Substring(startIndex); - string strRet = null; + string strRet; //Console.WriteLine(strRest); if (strEnd == null) { @@ -116,39 +121,39 @@ static string ParseString(string str, string strStart, string strEnd, out string return strRet; } - static StreamWriter swPassZero = null; - static string strPassZeroLog = "pass-zero.txt"; - static int totalProcs = 0; - static int totalNodes = 0; - static int procsPerNode = 0; + private static StreamWriter swPassZero = null; + private static string strPassZeroLog = "pass-zero.txt"; + private static int totalProcs = 0; + private static int totalNodes = 0; + private static int procsPerNode = 0; // this is qpf / 1000 so we calculate ms instead of s. - static UInt64 qpfAdjusted = 0; + private static ulong qpfAdjusted = 0; // we log the current qpc so subtract by this. - static UInt64 qpcStart = 0; - static Int32 totalAllocThreads = 0; - static Dictionary threadMapping = new Dictionary(112); + private static ulong qpcStart = 0; + private static int totalAllocThreads = 0; + private static Dictionary threadMapping = new Dictionary(112); // We do compress the samples, this stores the aggregated sample counts for all procs. - static int[] aggregatedSampleCount = null; + private static int[] aggregatedSampleCount = null; - static int timeUnitMS = 1; + private static int timeUnitMS = 1; // We wanna compress the log by compressing the samples in the same unit of time. // We get samples by procs, so on the same proc if we observe the same thread // allocating on the same alloc heap in the same time unit we simply count the total // samples. However we do want to remember if any of the p/m/i is set. - static string strLastTID = null; - static string strTID = null; - static string strLastThreadAllocHeap = null; - static string strAllocHeap = null; - static string strIdealProc = null; - static int lastTimeUnit = 0; - static int lastThreadSampleCount = 0; - static int lastThreadPCount = 0; - static int lastThreadMCount = 0; - static int lastThreadICount = 0; - static int lastProcIndex = -1; - static int largestTimeInBetweenGCs = 0; - - static void InitSampleInfoPassZero() + private static string strLastTID = null; + private static string strTID = null; + private static string strLastThreadAllocHeap = null; + private static string strAllocHeap = null; + private static string strIdealProc = null; + private static int lastTimeUnit = 0; + private static int lastThreadSampleCount = 0; + private static int lastThreadPCount = 0; + private static int lastThreadMCount = 0; + private static int lastThreadICount = 0; + private static int lastProcIndex = -1; + private static int largestTimeInBetweenGCs = 0; + + private static void InitSampleInfoPassZero() { strLastTID = null; strTID = null; @@ -161,7 +166,7 @@ static void InitSampleInfoPassZero() lastThreadICount = 0; } - static void LogPassZeroAggregatedSample(int _lastTimeUnit, int _tid, int _sampleCount, string _strAllocHeap) + private static void LogPassZeroAggregatedSample(int _lastTimeUnit, int _tid, int _sampleCount, string _strAllocHeap) { swPassZero.WriteLine("{0}ms,{1}({2}),{3},m:{4},p:{5},i:{6}({7})", _lastTimeUnit, @@ -177,7 +182,7 @@ static void LogPassZeroAggregatedSample(int _lastTimeUnit, int _tid, int _sample // Aside from writing out a new log this also prints out some stats for further processing: // How many samples are observed on each proc. Because we could use this for many proc analysis, // it's impractical to have a wide enough window to display all procs. - static void PassZero(string strLog) + private static void PassZero(string strLog) { swPassZero = new StreamWriter(strPassZeroLog); string strTemp, strRemaining; @@ -187,7 +192,7 @@ static void PassZero(string strLog) while ((s = sr.ReadLine()) != null) { - if (s.StartsWith("[")) + if (s.StartsWith('[')) { string strAfterTID = ParseString(s, "]", null, out strRemaining); //Console.WriteLine(s); @@ -196,18 +201,18 @@ static void PassZero(string strLog) { // [15900]qpf=10000000, start: 17262240813778(1726224081) strTemp = ParseString(s, "qpf=", ",", out strRemaining); - qpfAdjusted = UInt64.Parse(strTemp) / 1000; + qpfAdjusted = ulong.Parse(strTemp) / 1000; strTemp = ParseString(strRemaining, "start:", "(", out strRemaining); - qpcStart = UInt64.Parse(strTemp); + qpcStart = ulong.Parse(strTemp); Console.WriteLine("QPF adjusted: {0}, init QPC: {1}", qpfAdjusted, qpcStart); } else if (strAfterTID.StartsWith("total: ")) { // [15900]total: 112, numa: 2 strTemp = ParseString(strAfterTID, "total: ", ",", out strRemaining); - totalProcs = Int32.Parse(strTemp); + totalProcs = int.Parse(strTemp); strTemp = ParseString(strRemaining, "numa: ", null, out strRemaining); - totalNodes = Int32.Parse(strTemp); + totalNodes = int.Parse(strTemp); Console.WriteLine("total procs: {0}, nodes: {1}", totalProcs, totalNodes); procsPerNode = totalProcs / totalNodes; swPassZero.WriteLine("P: {0}, N: {1}", totalProcs, totalNodes); @@ -260,7 +265,7 @@ static void PassZero(string strLog) //Console.WriteLine(strAfterTID); swPassZero.WriteLine(strAfterTID); strTemp = ParseString(strAfterTID, "p", "]", out strRemaining); - lastProcIndex = Int32.Parse(strTemp); + lastProcIndex = int.Parse(strTemp); } else if (strAfterTID.StartsWith("[GCA#")) { @@ -272,11 +277,11 @@ static void PassZero(string strLog) // convert the raw ts to relative ms. strTemp = ParseString(strAfterTID, "-", "-", out strRemaining); //Console.WriteLine("min ts is {0}", strTemp); - UInt64 minTimestampMS = UInt64.Parse(strTemp); + ulong minTimestampMS = ulong.Parse(strTemp); minTimestampMS /= qpfAdjusted; strTemp = ParseString(strRemaining, "-", "]", out strRemaining); //Console.WriteLine("max ts is {0}", strTemp); - UInt64 maxTimestampMS = UInt64.Parse(strTemp); + ulong maxTimestampMS = ulong.Parse(strTemp); maxTimestampMS /= qpfAdjusted; strTemp = ParseString(strAfterTID, null, "-", out strRemaining); swPassZero.WriteLine("{0}-{1}-{2}", strTemp, minTimestampMS, maxTimestampMS); @@ -320,8 +325,8 @@ static void PassZero(string strLog) // strTemp = ParseString(s, "]", ",", out strRemaining); //UInt64 currentQPC = UInt64.Parse(strTemp); - UInt64 currentQPC = 0; - if (!UInt64.TryParse(strTemp, out currentQPC)) + ulong currentQPC = 0; + if (!ulong.TryParse(strTemp, out currentQPC)) { continue; } @@ -339,6 +344,7 @@ static void PassZero(string strLog) { Console.WriteLine(s); } + if (!threadMapping.ContainsKey(strTID)) { threadMapping.Add(strTID, totalAllocThreads); @@ -355,14 +361,14 @@ static void PassZero(string strLog) bool alloc_count_p = true; bool set_ideal_p = false; - if (strRemaining.Contains("|")) + if (strRemaining.Contains('|')) { strAllocHeap = ParseString(strRemaining, ",", "|", out strRemaining); - if (strRemaining.Contains("m")) + if (strRemaining.Contains('m')) multiple_procs_p = true; - if (strRemaining.Contains("p")) + if (strRemaining.Contains('p')) alloc_count_p = false; - if (strRemaining.Contains("i")) + if (strRemaining.Contains('i')) set_ideal_p = true; } else @@ -495,45 +501,45 @@ static void PassZero(string strLog) //========================================================================= // Pass one. TODO: should separate this from pass zero. //========================================================================= - static StreamWriter[] swPassOneFiles = null; + private static StreamWriter[] swPassOneFiles = null; // It's difficult to print all of the procs on one line so we only print per node. - static int nodeIndexToPrint = -1; - static bool fIncludeAllTime = false; - static bool fPrintThreadInfoPerTimeUnit = false; + private static int nodeIndexToPrint = -1; + private static bool fIncludeAllTime = false; + private static bool fPrintThreadInfoPerTimeUnit = false; // This represents the samples for all the procs inbetween GCs so it doesn't grow very large. // Cleared every GC. - static SampleInfo[][] samples; + private static SampleInfo[][] samples; // This is the min/max time for inbetween each GC. - static int startTimeMS = 0; - static int endTimeMS = 0; - static int currentProcIndex = -1; - static int lastTimeIndex = -1; + private static int startTimeMS = 0; + private static int endTimeMS = 0; + private static int currentProcIndex = -1; + private static int lastTimeIndex = -1; // This respresents the threads we see in samples so we can get info such as when threads // start running and how active they are. // Cleared every GC. - static List threadsSeenPerTimeUnit = new List(56); - static Dictionary threadsSeen = new Dictionary(56); - static Dictionary threadsSeenTotal = new Dictionary(56); - static List threadsToPrint = new List(8); - static string strThreadIndices = null; - static int gcIndexToPrintStart = -1; - static int gcIndexToPrintEnd = 1000000; - static string strGCRange = null; - static int totalGCCount = 0; - static int totalGCDurationMS = 0; - static int totalAllocMB = 0; - static int totalBudgetMB = 0; + private static List threadsSeenPerTimeUnit = new List(56); + private static Dictionary threadsSeen = new Dictionary(56); + private static Dictionary threadsSeenTotal = new Dictionary(56); + private static List threadsToPrint = new List(8); + private static string strThreadIndices = null; + private static int gcIndexToPrintStart = -1; + private static int gcIndexToPrintEnd = 1000000; + private static string strGCRange = null; + private static int totalGCCount = 0; + private static int totalGCDurationMS = 0; + private static int totalAllocMB = 0; + private static int totalBudgetMB = 0; // This represents allocated MB on each heap; parse from the lines following [GC_alloc_mb] - static int[] AllocMB; - static int budgetMB = 0; + private static int[] AllocMB; + private static int budgetMB = 0; // These are subscript chars for 0-9. - static char[] unicodeChars = { '\x2080', '\x2081', '\x2082', '\x2083', '\x2084', '\x2085', '\x2086', '\x2087', '\x2088', '\x2089' }; - static string[] passOneFileTypes = { "thread", "alloc" }; + private static char[] unicodeChars = ['\x2080', '\x2081', '\x2082', '\x2083', '\x2084', '\x2085', '\x2086', '\x2087', '\x2088', '\x2089']; + private static string[] passOneFileTypes = ["thread", "alloc"]; - static void PrintToAllPassOneFiles(string strLine) + private static void PrintToAllPassOneFiles(string strLine) { for (int fileIndex = 0; fileIndex < (int)PassOneViewType.MaxType; fileIndex++) { @@ -541,13 +547,12 @@ static void PrintToAllPassOneFiles(string strLine) } } - static void PrintAllocToAllPassOneFiles() + private static void PrintAllocToAllPassOneFiles() { string strAlloc = string.Format("{0,6}", nodeIndexToPrint); int procStart = procsPerNode * nodeIndexToPrint; int procEnd = procsPerNode * (nodeIndexToPrint + 1); - int currentGCAllocMBAllHeaps = 0; int currentGCAllocMB = 0; for (int procIndex = procStart; procIndex < procEnd; procIndex++) { @@ -557,7 +562,6 @@ static void PrintAllocToAllPassOneFiles() // currentGCAllocMB); } - currentGCAllocMBAllHeaps = currentGCAllocMB; currentGCAllocMB /= procsPerNode; totalAllocMB += currentGCAllocMB; totalBudgetMB += budgetMB; @@ -578,7 +582,8 @@ static void PrintAllocToAllPassOneFiles() strAlloc += string.Format("|"); PrintToAllPassOneFiles(strAlloc); } - static void CloseAllPassOneFiles() + + private static void CloseAllPassOneFiles() { Console.WriteLine("Total {0} GCs, avg {1}ms, node {2}: BFR %{3:f2}", totalGCCount, @@ -592,7 +597,7 @@ static void CloseAllPassOneFiles() } } - static void PrintHeader() + private static void PrintHeader() { // TEMP.. //procsPerNode = 16; @@ -613,7 +618,7 @@ static void PrintHeader() } // Note that I'm only allocating 2 chars for count and we don't expect to have more than 99 of them. - static string FormatCount(int count) + private static string FormatCount(int count) { if (count > 99) { @@ -632,7 +637,7 @@ static string FormatCount(int count) return strFormattedCount; } - static string FormatFlags(int flags) + private static string FormatFlags(int flags) { string strFormattedFlags = ""; if ((flags & (int)HeapBalanceFlagMask.MutipleProcs) != 0) @@ -665,7 +670,7 @@ static string FormatFlags(int flags) // We take the last one on the same time unless we see one with interesting // info (ie, one of m/p/i isn't 0); or one with higher sample count. // - static void PrintProcActivityOnNode() + private static void PrintProcActivityOnNode() { // TEMP.. //procsPerNode = 16; @@ -775,18 +780,18 @@ static void PrintProcActivityOnNode() //} } - static int GetAdjustedProcIndex(int pIndex) + private static int GetAdjustedProcIndex(int pIndex) { return (pIndex % procsPerNode); } - static void ParseThreadIndices(string _strThreadIndices) + private static void ParseThreadIndices(string _strThreadIndices) { strThreadIndices = _strThreadIndices; - string[] fields = strThreadIndices.Split(new Char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] fields = strThreadIndices.Split([','], StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < fields.Length; i++) { - threadsToPrint.Add(Int32.Parse(fields[i])); + threadsToPrint.Add(int.Parse(fields[i])); } int len = threadsToPrint.Count; @@ -797,11 +802,11 @@ static void ParseThreadIndices(string _strThreadIndices) } } - static void ParseGCRange(string _strGCRange) + private static void ParseGCRange(string _strGCRange) { strGCRange = _strGCRange; - int dashIndex= strGCRange.IndexOf('-'); - if (dashIndex== -1) + int dashIndex = strGCRange.IndexOf('-'); + if (dashIndex == -1) { Console.WriteLine("Invalid GC range {0}", strGCRange); return; @@ -811,20 +816,20 @@ static void ParseGCRange(string _strGCRange) string strEnd = strGCRange.Substring(dashIndex + 1); if (strStart != "start") - gcIndexToPrintStart = Int32.Parse(strStart); + gcIndexToPrintStart = int.Parse(strStart); if (strEnd != "end") - gcIndexToPrintEnd = Int32.Parse(strEnd); + gcIndexToPrintEnd = int.Parse(strEnd); Console.WriteLine("printing GC {0}->{1}", gcIndexToPrintStart, gcIndexToPrintEnd); } - static void PassOne(string strPassZeroLog) + private static void PassOne(string strPassZeroLog) { // This generates 2 files - thread view and alloc heap view. This is just so you could // compare them SxS. string strLogNameWithoutExtension = Path.GetFileNameWithoutExtension(strPassZeroLog); string strIncludeAll = (fIncludeAllTime ? "-ia1-" : "-ia0-"); - string strTI = ((strThreadIndices == null) ? "" : strThreadIndices); + string strTI = strThreadIndices ?? ""; string strPassOneLog = strLogNameWithoutExtension + "-pass1-n" + nodeIndexToPrint + strIncludeAll + strTI; if (strGCRange != null) @@ -848,9 +853,9 @@ static void PassOne(string strPassZeroLog) { //P: 112, N: 2 strTemp = ParseString(s, "P: ", ",", out strRemaining); - totalProcs = Int32.Parse(strTemp); + totalProcs = int.Parse(strTemp); strTemp = ParseString(strRemaining, "N: ", null, out strRemaining); - totalNodes = Int32.Parse(strTemp); + totalNodes = int.Parse(strTemp); procsPerNode = totalProcs / totalNodes; PrintHeader(); @@ -898,7 +903,7 @@ static void PassOne(string strPassZeroLog) // GCA#gc_index time_since_last_gc-min_time-max_time //[GCA#1 270-42-268 strTemp = ParseString(s, "A#", " ", out strRemaining); - int gcIndex = Int32.Parse(strTemp); + int gcIndex = int.Parse(strTemp); fSkip = !((gcIndex >= gcIndexToPrintStart) && (gcIndex <= gcIndexToPrintEnd)); @@ -913,9 +918,9 @@ static void PassOne(string strPassZeroLog) } //swPassOne.WriteLine("[GC#{0}-GC#{1}]", (gcIndex - 1), gcIndex); strTemp = ParseString(s, "-", "-", out strRemaining); - startTimeMS = Int32.Parse(strTemp); + startTimeMS = int.Parse(strTemp); strTemp = ParseString(strRemaining, "-", null, out strRemaining); - endTimeMS = Int32.Parse(strTemp); + endTimeMS = int.Parse(strTemp); //Console.WriteLine("Before GC#{0} time {1} {2}, {3} entries", // gcIndex, startTimeMS, endTimeMS, ((endTimeMS - startTimeMS) / timeUnitMS)); } @@ -926,7 +931,7 @@ static void PassOne(string strPassZeroLog) //[p78]-192-225ms strTemp = ParseString(s, "[p", "]", out strRemaining); - currentProcIndex = Int32.Parse(strTemp); + currentProcIndex = int.Parse(strTemp); lastTimeIndex = -1; //currentProcIndex = GetAdjustedProcIndex(currentProcIndex); } @@ -946,13 +951,13 @@ static void PassOne(string strPassZeroLog) //Console.WriteLine(s); int procIndexBase = totalNodesRead * procsPerNode; strTemp = ParseString(s, "[N#", "]", out strRemaining); - budgetMB = Int32.Parse(strTemp); + budgetMB = int.Parse(strTemp); string strAllocLine = s.Substring(7); //Console.WriteLine("spliting {0}", strAllocLine); - string[] fieldsAlloc = strAllocLine.Split(new Char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] fieldsAlloc = strAllocLine.Split([','], StringSplitOptions.RemoveEmptyEntries); for (int fieldIndex = 0; fieldIndex < procsPerNode; fieldIndex++) { - AllocMB[procIndexBase + fieldIndex] = Int32.Parse(fieldsAlloc[fieldIndex]); + AllocMB[procIndexBase + fieldIndex] = int.Parse(fieldsAlloc[fieldIndex]); } totalNodesRead++; if (totalNodesRead == totalNodes) @@ -973,7 +978,7 @@ static void PassOne(string strPassZeroLog) //[GC#1-15-2911711] strTemp = ParseString(s, "-", "-", out strRemaining); totalGCCount++; - totalGCDurationMS += Int32.Parse(strTemp); + totalGCDurationMS += int.Parse(strTemp); //Console.WriteLine(s); //Console.WriteLine("GC#{0} {1}ms ->total {2}ms", totalGCCount, Int32.Parse(strTemp), totalGCDurationMS); @@ -993,7 +998,7 @@ static void PassOne(string strPassZeroLog) // Majority of the log, (83) is the ideal proc for that thread. //1051ms,95(1),83,m:0,p:0,i:0(83) strTemp = ParseString(s, null, "ms", out strRemaining); - int currentTimeIndex = Int32.Parse(strTemp); + int currentTimeIndex = int.Parse(strTemp); if (currentTimeIndex < lastTimeIndex) { @@ -1006,19 +1011,19 @@ static void PassOne(string strPassZeroLog) currentTimeIndex -= startTimeMS; currentTimeIndex /= timeUnitMS; strTemp = ParseString(strRemaining, ",", "(", out strRemaining); - int tid = Int32.Parse(strTemp); + int tid = int.Parse(strTemp); strTemp = ParseString(strRemaining, "(", ")", out strRemaining); - int countSamples = Int32.Parse(strTemp); + int countSamples = int.Parse(strTemp); strTemp = ParseString(strRemaining, ",", ",", out strRemaining); - int allocHeap = Int32.Parse(strTemp); + int allocHeap = int.Parse(strTemp); strTemp = ParseString(strRemaining, ",m:", ",", out strRemaining); - int flags = Int32.Parse(strTemp); + int flags = int.Parse(strTemp); strTemp = ParseString(strRemaining, ",p:", ",", out strRemaining); - flags |= Int32.Parse(strTemp) << 4; + flags |= int.Parse(strTemp) << 4; strTemp = ParseString(strRemaining, ",i:", "(", out strRemaining); - flags |= Int32.Parse(strTemp) << 8; + flags |= int.Parse(strTemp) << 8; strTemp = ParseString(strRemaining, "(", ")", out strRemaining); - int idealProcNo = Int32.Parse(strTemp); + int idealProcNo = int.Parse(strTemp); //Console.WriteLine("ADDING time {0}ms, entry {1}, thread #{2}, ah {3}, ideal {4}", // currentTimeIndex, currentProcIndex, tid, allocHeap, idealProcNo); samples[currentTimeIndex][currentProcIndex] = new SampleInfo(tid, allocHeap, countSamples, flags, idealProcNo); @@ -1040,9 +1045,8 @@ static void PassOne(string strPassZeroLog) // TODO: in pass zero there's merit in assigning thread indices based on the first CPU they appear on, // instead of just assigning one as we come across them. - static void Main(string[] args) + private static void Main(string[] args) { - int len = args.Length; string strLog = null; for (int i = 0; i < args.Length; ++i) @@ -1056,7 +1060,7 @@ static void Main(string[] args) else if (currentArg.Equals("-ia") || currentArg.Equals("-IncludeAll")) { currentArgValue = args[++i]; - fIncludeAllTime = (Int32.Parse(currentArgValue) == 1); + fIncludeAllTime = (int.Parse(currentArgValue) == 1); } else if (currentArg.Equals("-ti") || currentArg.Equals("-ThreadIndices")) { @@ -1080,7 +1084,7 @@ static void Main(string[] args) // total procs they ran on during that time. This shows us how volatile threads // are jumping between procs. currentArgValue = args[++i]; - if (Int32.Parse(currentArgValue) == 1) + if (int.Parse(currentArgValue) == 1) fPrintThreadInfoPerTimeUnit = true; } } diff --git a/src/coreclr/tools/GCLogParser/parse-hb-log.csproj b/src/coreclr/tools/GCLogParser/parse-hb-log.csproj index 5bff6087f5109..476218ce55e7a 100644 --- a/src/coreclr/tools/GCLogParser/parse-hb-log.csproj +++ b/src/coreclr/tools/GCLogParser/parse-hb-log.csproj @@ -1,66 +1,9 @@ - - - + + - Debug - AnyCPU - {6AE26CD6-C971-48D6-8C03-2FFA272B942C} Exe - parse_hb_log - parse-hb-log - v4.7.2 - true - true + $(NetCoreAppToolCurrent) + $(NoWarn);CA1805;CA1854;CA1864; - - AnyCPU - true - full - false - artifacts\Debug\ - DEBUG;TRACE - - - AnyCPU - pdbonly - true - artifacts\Release\ - TRACE - - - true - artifacts\x64\Debug\ - DEBUG;TRACE - full - x64 - MinimumRecommendedRules.ruleset - true - - - artifacts\x64\Release\ - TRACE - true - pdbonly - x64 - MinimumRecommendedRules.ruleset - true - - - - - - - - - - - - - - - - - - - + diff --git a/src/coreclr/tools/SOS/SOS_README.md b/src/coreclr/tools/SOS/SOS_README.md index d20ab2d44f8ef..f034cd093138b 100644 --- a/src/coreclr/tools/SOS/SOS_README.md +++ b/src/coreclr/tools/SOS/SOS_README.md @@ -2,4 +2,4 @@ SOS and other diagnostic tools now ship out of band and work with any version of SOS has moved to the diagnostics repo here: https://github.com/dotnet/diagnostics.git. -Instructions to install SOS: https://docs.microsoft.com/dotnet/core/diagnostics/dotnet-sos +Instructions to install SOS: https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-sos diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributesOnEntityNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributesOnEntityNode.cs index 31b54535294a9..7eb0cb4981539 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributesOnEntityNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributesOnEntityNode.cs @@ -22,7 +22,7 @@ namespace ILCompiler.DependencyAnalysis { /// /// Computes the list of dependencies from DynamicDependencyAttribute. - /// https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute + /// https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute /// public class DynamicDependencyAttributesOnEntityNode : DependencyNodeCore { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs index e98c243b2b726..e3eb154d8d0c3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Internal.Text; using Internal.TypeSystem; using ILCompiler.DependencyAnalysisFramework; @@ -305,20 +304,13 @@ public bool Equals(VertexSequenceKey other) return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - // This is expected to be optimized into a single rotl instruction - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - public override int GetHashCode() { int hashcode = 0; foreach (NativeLayoutVertexNode node in Vertices) { hashcode ^= node.GetHashCode(); - hashcode = _rotl(hashcode, 5); + hashcode = int.RotateLeft(hashcode, 5); } return hashcode; } @@ -345,20 +337,13 @@ bool IEqualityComparer>.Equals(List x, List y) return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - // This is expected to be optimized into a single rotl instruction - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - int IEqualityComparer>.GetHashCode(List obj) { int hashcode = 0x42284781; foreach (uint u in obj) { hashcode ^= (int)u; - hashcode = _rotl(hashcode, 5); + hashcode = int.RotateLeft(hashcode, 5); } return hashcode; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs index f319eebfe6ce1..8bbdccb9278a0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs @@ -26,7 +26,7 @@ namespace ILCompiler.ObjectWriter /// /// /// The PE/COFF object format is described in the official specifciation at - /// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format. However, + /// https://learn.microsoft.com/windows/win32/debug/pe-format. However, /// numerous extensions are missing in the specification. The most notable /// ones are listed below. /// diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs index a875e55b82bad..248bdf891243c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs @@ -136,11 +136,22 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) offsetsToVisit.Push(ehRegion.FilterOffset); offsetsToVisit.Push(ehRegion.HandlerOffset); + + if ((uint)ehRegion.TryLength >= (uint)methodBytes.Length + || (uint)ehRegion.HandlerLength >= (uint)methodBytes.Length + || ((uint)methodBytes.Length - (uint)ehRegion.TryLength) < (uint)ehRegion.TryOffset + || ((uint)methodBytes.Length - (uint)ehRegion.HandlerLength) < (uint)ehRegion.HandlerOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } } // Identify basic blocks and instruction boundaries while (offsetsToVisit.TryPop(out int offset)) { + if ((uint)offset >= (uint)flags.Length) + ThrowHelper.ThrowInvalidProgramException(); + // If this was already visited, we're done if (flags[offset] != 0) { @@ -415,7 +426,8 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) return method; // Maps instruction offsets in original method body to offsets in rewritten method body. - var offsetMap = new int[methodBytes.Length]; + // Do a + 1 to length because exception handlers might refer to offset at the end of last instruction. + var offsetMap = new int[methodBytes.Length + 1]; #if DEBUG Array.Fill(offsetMap, -1); #endif @@ -469,6 +481,7 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) dstPos += 2; } } + offsetMap[methodBytes.Length] = dstPos; // Now generate the new body var newBody = new byte[dstPos]; 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 d3a5e5a80b0fd..2e60440293983 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -386,6 +386,16 @@ private void ImportCall(ILOpcode opcode, int token) } return; } + + if (IsRuntimeHelpersIsReferenceOrContainsReferences(method)) + { + return; + } + + if (IsMemoryMarshalGetArrayDataReference(method)) + { + return; + } } TypeDesc exactType = method.OwningType; @@ -1391,6 +1401,34 @@ private static bool IsEETypePtrOf(MethodDesc method) return false; } + private static bool IsRuntimeHelpersIsReferenceOrContainsReferences(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "IsReferenceOrContainsReferences" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "RuntimeHelpers" && owningType.Namespace == "System.Runtime.CompilerServices"; + } + } + + return false; + } + + private static bool IsMemoryMarshalGetArrayDataReference(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "GetArrayDataReference" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "MemoryMarshal" && owningType.Namespace == "System.Runtime.InteropServices"; + } + } + + return false; + } + private DefType GetWellKnownType(WellKnownType wellKnownType) { return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs index 354b7913d533e..02c35466167aa 100644 --- a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs @@ -241,6 +241,10 @@ private static SignatureCallingConvention GetSignatureCallingConvention(Cts.Meth { callingConvention |= SignatureCallingConvention.HasThis; } + if ((signature.Flags & Cts.MethodSignatureFlags.ExplicitThis) != 0) + { + callingConvention |= SignatureCallingConvention.ExplicitThis; + } return callingConvention; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index 4cf6d65ea4390..b1bd46ae269c9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -818,10 +818,24 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada { if (type.IsExplicitLayout) { + // Works around https://github.com/dotnet/runtime/issues/102868 + if (!type.IsValueType && + (type.MetadataBaseType is MetadataType baseType && baseType.IsSequentialLayout)) + { + ThrowHelper.ThrowTypeLoadException(type); + } + return ComputeExplicitFieldLayout(type, numInstanceFields); } else if (type.IsSequentialLayout && !type.ContainsGCPointers) { + // Works around https://github.com/dotnet/runtime/issues/102868 + if (!type.IsValueType && + (type.MetadataBaseType is MetadataType baseType && baseType.IsExplicitLayout)) + { + ThrowHelper.ThrowTypeLoadException(type); + } + return ComputeSequentialFieldLayout(type, numInstanceFields); } else diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 88317684fe882..d9d90ddfd19cf 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2385,7 +2385,7 @@ namespace Util { #ifdef HOST_WINDOWS // Struct used to scope suspension of client impersonation for the current thread. - // https://docs.microsoft.com/en-us/windows/desktop/secauthz/client-impersonation + // https://learn.microsoft.com/windows/desktop/secauthz/client-impersonation class SuspendImpersonation { public: diff --git a/src/coreclr/vm/amsi.cpp b/src/coreclr/vm/amsi.cpp index 4e2a36c130d1d..698aa229e8e6c 100644 --- a/src/coreclr/vm/amsi.cpp +++ b/src/coreclr/vm/amsi.cpp @@ -9,7 +9,7 @@ namespace { - // https://docs.microsoft.com/en-us/windows/desktop/api/amsi/ + // https://learn.microsoft.com/windows/desktop/api/amsi/ DECLARE_HANDLE(HAMSICONTEXT); DECLARE_HANDLE(HAMSISESSION); diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 045598ed97187..3ee0387c776a4 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -304,7 +304,7 @@ inline TADDR GetMem(PCODE address, SIZE_T size, bool signExtend) } EX_CATCH { - mem = NULL; + mem = 0; _ASSERTE(!"Memory read within jitted Code Failed, this should not happen!!!!"); } EX_END_CATCH(SwallowAllExceptions); diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index ead7a01e975ce..de33ce61be849 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -295,7 +295,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, context.X27 = unwoundstate->captureX19_X29[8] = baseState->captureX19_X29[8]; context.X28 = unwoundstate->captureX19_X29[9] = baseState->captureX19_X29[9]; context.Fp = unwoundstate->captureX19_X29[10] = baseState->captureX19_X29[10]; - context.Lr = NULL; // Filled by the unwinder + context.Lr = 0; // Filled by the unwinder context.Sp = baseState->captureSp; context.Pc = baseState->captureIp; @@ -316,7 +316,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, nonVolContextPtrs.X27 = &unwoundstate->captureX19_X29[8]; nonVolContextPtrs.X28 = &unwoundstate->captureX19_X29[9]; nonVolContextPtrs.Fp = &unwoundstate->captureX19_X29[10]; - nonVolContextPtrs.Lr = NULL; // Filled by the unwinder + nonVolContextPtrs.Lr = 0; // Filled by the unwinder #endif // DACCESS_COMPILE @@ -464,7 +464,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContext->X27 = (DWORD64)(pUnwoundState->captureX19_X29[8]); pRD->pCurrentContext->X28 = (DWORD64)(pUnwoundState->captureX19_X29[9]); pRD->pCurrentContext->Fp = (DWORD64)(pUnwoundState->captureX19_X29[10]); - pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC + pRD->pCurrentContext->Lr = 0; // Unwind again to get Caller's PC pRD->pCurrentContextPointers->X19 = &pRD->pCurrentContext->X19; pRD->pCurrentContextPointers->X20 = &pRD->pCurrentContext->X20; @@ -503,7 +503,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContext->X27 = (DWORD64)(m_MachState.unwoundX19_X29[8]); pRD->pCurrentContext->X28 = (DWORD64)(m_MachState.unwoundX19_X29[9]); pRD->pCurrentContext->Fp = (DWORD64)(m_MachState.unwoundX19_X29[10]); - pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC + pRD->pCurrentContext->Lr = 0; // Unwind again to get Caller's PC #else // __APPLE__ pRD->pCurrentContext->X19 = *m_MachState.ptrX19_X29[0]; pRD->pCurrentContext->X20 = *m_MachState.ptrX19_X29[1]; @@ -516,7 +516,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContext->X27 = *m_MachState.ptrX19_X29[8]; pRD->pCurrentContext->X28 = *m_MachState.ptrX19_X29[9]; pRD->pCurrentContext->Fp = *m_MachState.ptrX19_X29[10]; - pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC + pRD->pCurrentContext->Lr = 0; // Unwind again to get Caller's PC #endif // __APPLE__ #if !defined(DACCESS_COMPILE) @@ -531,7 +531,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContextPointers->X27 = m_MachState.ptrX19_X29[8]; pRD->pCurrentContextPointers->X28 = m_MachState.ptrX19_X29[9]; pRD->pCurrentContextPointers->Fp = m_MachState.ptrX19_X29[10]; - pRD->pCurrentContextPointers->Lr = NULL; // Unwind again to get Caller's PC + pRD->pCurrentContextPointers->Lr = 0; // Unwind again to get Caller's PC #endif ClearRegDisplayArgumentAndScratchRegisters(pRD); diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 0885ec6d3b8ed..d115a22850a74 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1011,11 +1011,11 @@ PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFuncti int size = 4; #if defined(TARGET_ARM) - // See https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling + // See https://learn.microsoft.com/cpp/build/arm-exception-handling int unwindWords = xdata[0] >> 28; int epilogScopes = (xdata[0] >> 23) & 0x1f; #else - // See https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + // See https://learn.microsoft.com/cpp/build/arm64-exception-handling int unwindWords = xdata[0] >> 27; int epilogScopes = (xdata[0] >> 22) & 0x1f; #endif @@ -1258,29 +1258,18 @@ void EEJitManager::SetCpuInfo() int cpuFeatures = minipal_getcpufeatures(); #if defined(TARGET_X86) || defined(TARGET_AMD64) - -#if defined(TARGET_X86) && !defined(TARGET_WINDOWS) - // Linux may still support no SSE/SSE2 for 32-bit - if ((cpuFeatures & XArchIntrinsicConstants_VectorT128) == 0) - { - EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("SSE and SSE2 processor support required.")); - } -#else - _ASSERTE((cpuFeatures & XArchIntrinsicConstants_VectorT128) != 0); -#endif - CPUCompileFlags.Set(InstructionSet_VectorT128); // Get the maximum bitwidth of Vector, rounding down to the nearest multiple of 128-bits uint32_t maxVectorTBitWidth = (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MaxVectorTBitWidth) / 128) * 128; - if (((cpuFeatures & XArchIntrinsicConstants_VectorT256) != 0) && ((maxVectorTBitWidth == 0) || (maxVectorTBitWidth >= 256))) + if (((cpuFeatures & XArchIntrinsicConstants_Avx2) != 0) && ((maxVectorTBitWidth == 0) || (maxVectorTBitWidth >= 256))) { // We allow 256-bit Vector by default CPUCompileFlags.Set(InstructionSet_VectorT256); } - if (((cpuFeatures & XArchIntrinsicConstants_VectorT512) != 0) && (maxVectorTBitWidth >= 512)) + if (((cpuFeatures & XArchIntrinsicConstants_Avx512f) != 0) && (maxVectorTBitWidth >= 512)) { // We require 512-bit Vector to be opt-in CPUCompileFlags.Set(InstructionSet_VectorT512); @@ -1458,12 +1447,12 @@ void EEJitManager::SetCpuInfo() #if !defined(TARGET_WINDOWS) // Linux may still support no AdvSimd - if ((cpuFeatures & ARM64IntrinsicConstants_VectorT128) == 0) + if ((cpuFeatures & ARM64IntrinsicConstants_AdvSimd) == 0) { EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("AdvSimd processor support required.")); } #else - _ASSERTE((cpuFeatures & ARM64IntrinsicConstants_VectorT128) != 0); + _ASSERTE((cpuFeatures & ARM64IntrinsicConstants_AdvSimd) != 0); #endif CPUCompileFlags.Set(InstructionSet_VectorT128); diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 7cfee71b92c9e..23dfb4d238803 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -507,11 +507,6 @@ FCIMPL1(void, ThreadNative::Initialize, ThreadBaseObject* pThisUNSAFE) PREFIX_ASSUME(unstarted != NULL); - if (AppDomain::GetCurrentDomain()->IgnoreUnhandledExceptions()) - { - unstarted->SetThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions); - } - pThis->SetInternal(unstarted); pThis->SetManagedThreadId(unstarted->GetThreadId()); unstarted->SetExposedObject(pThis); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 17f74503bdc28..df42e52eab952 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -632,7 +632,6 @@ DEFINE_CLASS(RTFIELD, Reflection, RtFieldInfo) DEFINE_METHOD(RTFIELD, GET_FIELDHANDLE, GetFieldHandle, IM_RetIntPtr) DEFINE_CLASS(RUNTIME_HELPERS, CompilerServices, RuntimeHelpers) -DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsReferenceOrContainsReferences, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) diff --git a/src/coreclr/vm/dacenumerablehash.inl b/src/coreclr/vm/dacenumerablehash.inl index eb8bd5df6a9c5..c2522e156647e 100644 --- a/src/coreclr/vm/dacenumerablehash.inl +++ b/src/coreclr/vm/dacenumerablehash.inl @@ -461,11 +461,6 @@ DPTR(VALUE) DacEnumerableHashTable::BaseFindNextEntryByHash( namespace HashTableDetail { - // Use the C++ detection idiom (https://isocpp.org/blog/2017/09/detection-idiom-a-stopgap-for-concepts-simon-brand) to call the - // derived table's EnumMemoryRegionsForEntry method if it defines one. - template struct make_void { using type = void; }; - template using void_t = typename make_void::type; - template struct negation : std::integral_constant { }; template diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 657fdd0750169..cefcdec06c1fd 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -161,6 +161,7 @@ void EEPolicy::HandleExitProcess(ShutdownCompleteAction sca) //--------------------------------------------------------------------------------------- class CallStackLogger { + PEXCEPTION_POINTERS m_pExceptionInfo; // MethodDescs of the stack frames, the TOS is at index 0 CDynArray m_frames; @@ -175,6 +176,16 @@ class CallStackLogger { WRAPPER_NO_CONTRACT; + if (m_pExceptionInfo != NULL) + { + // Skip managed frames that are not part of the stack that caused the exception + // (they are part of the stack that is unwinding the exception) + if (GetRegdisplaySP(pCF->GetRegisterSet()) < GetSP(m_pExceptionInfo->ContextRecord)) + { + return SWA_CONTINUE; + } + } + MethodDesc *pMD = pCF->GetFunction(); if (m_commonStartIndex != -1) @@ -231,6 +242,13 @@ class CallStackLogger public: + CallStackLogger(PEXCEPTION_POINTERS pExceptionInfo) + { + WRAPPER_NO_CONTRACT; + + m_pExceptionInfo = pExceptionInfo; + } + // Callback called by the stack walker for each frame on the stack static StackWalkAction LogCallstackForLogCallback(CrawlFrame *pCF, VOID* pData) { @@ -275,7 +293,7 @@ class CallStackLogger // Return Value: // None // -inline void LogCallstackForLogWorker(Thread* pThread) +inline void LogCallstackForLogWorker(Thread* pThread, PEXCEPTION_POINTERS pExceptionInfo) { WRAPPER_NO_CONTRACT; @@ -291,7 +309,7 @@ inline void LogCallstackForLogWorker(Thread* pThread) } WordAt.Append(W(" ")); - CallStackLogger logger; + CallStackLogger logger(pExceptionInfo); pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK); @@ -312,7 +330,7 @@ inline void LogCallstackForLogWorker(Thread* pThread) // Return Value: // None // -void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, LPCWSTR errorSource, LPCWSTR argExceptionString) +void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString) { WRAPPER_NO_CONTRACT; @@ -379,7 +397,7 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, LPCWSTR errorSource Thread* pThread = GetThreadNULLOk(); if (pThread && errorSource == NULL) { - LogCallstackForLogWorker(pThread); + LogCallstackForLogWorker(pThread, pExceptionInfo); if (argExceptionString != NULL) { PrintToStdErrW(argExceptionString); @@ -407,7 +425,7 @@ void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage _ASSERTE(pExceptionInfo != NULL); // Log exception to StdErr - LogInfoForFatalError(exitCode, pszMessage, errorSource, argExceptionString); + LogInfoForFatalError(exitCode, pszMessage, pExceptionInfo, errorSource, argExceptionString); if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, FailFast)) { @@ -579,7 +597,7 @@ void DisplayStackOverflowException() DWORD LogStackOverflowStackTraceThread(void* arg) { - LogCallstackForLogWorker((Thread*)arg); + LogCallstackForLogWorker((Thread*)arg, NULL); return 0; } diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index cb1fe1e30a449..c29505dc1a177 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4720,31 +4720,18 @@ LONG InternalUnhandledExceptionFilter_Worker( if (pParam->pThread != NULL) { - BOOL fIsProcessTerminating = TRUE; - // In CoreCLR, we can be asked to not let an exception go unhandled on managed threads in a given AppDomain. + // In CoreCLR, we can be asked to not let an exception go unhandled on managed threads. // If the exception reaches the top of the thread's stack, we simply deliver AppDomain's UnhandledException event and // return back to the filter, instead of letting the process terminate because of unhandled exception. - // Below is how we perform the check: - // - // 1) The flag is specified on the AD when it is created by the host and all managed threads created - // in such an AD will inherit the flag. For non-finalizer and non-threadpool threads, we check the flag against the thread. - // 2) The finalizer thread always switches to the AD of the object that is going to be finalized. Thus, - // while it wont have the flag specified, the AD it switches to will. - // 3) The threadpool thread also switches to the correct AD before executing the request. The thread wont have the - // flag specified, but the AD it switches to will. - // This code must only be exercised when running as a normal filter; returning // EXCEPTION_EXECUTE_HANDLER is not valid if this code is being invoked from // the UEF. // Fortunately, we should never get into this case, since the thread flag about // ignoring unhandled exceptions cannot be set on the default domain. - if (IsFinalizerThread() || (pParam->pThread->IsThreadPoolThread())) - fIsProcessTerminating = !(AppDomain::GetCurrentDomain()->IgnoreUnhandledExceptions()); - else - fIsProcessTerminating = !(pParam->pThread->HasThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions)); + BOOL fIsProcessTerminating = !(AppDomain::GetCurrentDomain()->IgnoreUnhandledExceptions()); #ifndef TARGET_UNIX // Setup the watson bucketing details for UE processing. diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 6033dd4ad02f2..b7e02a436de96 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -7679,6 +7679,28 @@ UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) #endif } +size_t GetSSPForFrameOnCurrentStack(CONTEXT *pContext) +{ + size_t *targetSSP = 0; + +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + targetSSP = (size_t *)_rdsspq(); + // Find the shadow stack pointer for the frame we are going to restore to. + // The SSP we search is pointing to the return address of the frame represented + // by the passed in context. So we search for the instruction pointer from + // the context and return one slot up from there. + if (targetSSP != NULL) + { + size_t ip = GetIP(pContext); + while (*targetSSP++ != ip) + { + } + } +#endif // HOST_AMD64 && HOST_WINDOWS + + return (size_t)targetSSP; +} + extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { QCALL_CONTRACT; @@ -7695,6 +7717,7 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); DWORD_PTR dwResumePC = 0; UINT_PTR callerTargetSp = 0; + size_t targetSSP = GetSSPForFrameOnCurrentStack(pvRegDisplay->pCurrentContext); if (pHandlerIP != NULL) { @@ -7815,13 +7838,13 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio SetIP(pvRegDisplay->pCurrentContext, uAbortAddr); } - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } else { if (fIntercepted) { - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } #ifdef HOST_UNIX if (propagateExceptionCallback) @@ -7831,13 +7854,20 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio UpdateContextForPropagationCallback(propagateExceptionCallback, propagateExceptionContext, pvRegDisplay->pCurrentContext); GCX_PREEMP_NO_DTOR(); - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } #endif // HOST_UNIX // Throw exception from the caller context #if defined(HOST_AMD64) ULONG64* returnAddress = (ULONG64*)targetSp; *returnAddress = pvRegDisplay->pCurrentContext->Rip; +#ifdef HOST_WINDOWS + if (targetSSP != 0) + { + targetSSP -= sizeof(size_t); + _wrssq(pvRegDisplay->pCurrentContext->Rip, (void*)targetSSP); + } +#endif // HOST_WINDOWS #elif defined(HOST_X86) ULONG32* returnAddress = (ULONG32*)targetSp; *returnAddress = pvRegDisplay->pCurrentContext->Eip; @@ -7872,7 +7902,7 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio pvRegDisplay->pCurrentContext->FIRST_ARG_REG = (size_t)OBJECTREFToObject(exceptionObj.Get()); #undef FIRST_ARG_REG - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } END_QCALL; return NULL; @@ -7918,8 +7948,10 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) _ASSERTE(FitsIn(ulRelOffset)); uResumePC = codeInfo.GetJitManager()->GetCodeAddressForRelOffset(codeInfo.GetMethodToken(), static_cast(ulRelOffset)); + size_t targetSSP = GetSSPForFrameOnCurrentStack(pvRegDisplay->pCurrentContext); + SetIP(pvRegDisplay->pCurrentContext, uResumePC); - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) @@ -8155,6 +8187,33 @@ static BOOL CheckExceptionInterception(StackFrameIterator* pStackFrameIterator, return isIntercepted; } +void FailFastIfCorruptingStateException(ExInfo *pExInfo) +{ + // Failfast if exception indicates corrupted process state + if (IsProcessCorruptedStateException(pExInfo->m_ExceptionCode, pExInfo->GetThrowable())) + { + OBJECTREF oThrowable = NULL; + SString message; + + GCPROTECT_BEGIN(oThrowable); + oThrowable = pExInfo->GetThrowable(); + if (oThrowable != NULL) + { + EX_TRY + { + GetExceptionMessage(oThrowable, message); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + } + GCPROTECT_END(); + + EEPolicy::HandleFatalError(pExInfo->m_ExceptionCode, 0, (LPCWSTR)message, dac_cast(&pExInfo->m_ptrs)); + } +} + static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo) { if (pExInfo->m_passNumber == 1) @@ -8295,6 +8354,8 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk { GCX_COOP(); UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); + + FailFastIfCorruptingStateException(pExInfo); } // Walk the stack until it finds the first managed method diff --git a/src/coreclr/vm/field.cpp b/src/coreclr/vm/field.cpp index 0b6d91359b73b..1481acf75dd9b 100644 --- a/src/coreclr/vm/field.cpp +++ b/src/coreclr/vm/field.cpp @@ -138,6 +138,7 @@ TypeHandle FieldDesc::LookupFieldTypeHandle(ClassLoadLevel level, BOOL dropGener type == ELEMENT_TYPE_STRING || type == ELEMENT_TYPE_TYPEDBYREF || type == ELEMENT_TYPE_SZARRAY || + type == ELEMENT_TYPE_ARRAY || type == ELEMENT_TYPE_VAR ); diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index e5e10b0fecae5..2f6293c894a07 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -2509,7 +2509,7 @@ HRESULT GetTypeLibVersionForAssembly( // Check to see if the TypeLibVersionAttribute is set. IfFailRet(pAssembly->GetMDImport()->GetCustomAttributeByName(TokenFromRid(1, mdtAssembly), INTEROP_TYPELIBVERSION_TYPE, (const void**)&pbData, &cbData)); - // For attribute contents, see https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.typelibversionattribute + // For attribute contents, see https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.typelibversionattribute if (cbData >= (2 + 2 * sizeof(UINT32))) { CustomAttributeParser cap(pbData, cbData); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 442bdd01627ad..d255c984e6826 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -7381,34 +7381,6 @@ bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn, mdMethodDef tk = ftn->GetMemberDef(); - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_REFERENCE_OR_CONTAINS_REFERENCES)->GetMemberDef()) - { - _ASSERTE(ftn->HasMethodInstantiation()); - Instantiation inst = ftn->GetMethodInstantiation(); - - _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); - TypeHandle typeHandle = inst[0]; - MethodTable * methodTable = typeHandle.GetMethodTable(); - - static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET }; - static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET }; - - if (!methodTable->IsValueType() || methodTable->ContainsPointers()) - { - methInfo->ILCode = const_cast(returnTrue); - } - else - { - methInfo->ILCode = const_cast(returnFalse); - } - - methInfo->ILCodeSize = sizeof(returnTrue); - methInfo->maxStack = 1; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_BITWISE_EQUATABLE)->GetMemberDef()) { _ASSERTE(ftn->HasMethodInstantiation()); @@ -14411,7 +14383,7 @@ HRESULT CEEInfo::getPgoInstrumentationResults( PgoInstrumentationSchema **pSchema, // pointer to the schema table which describes the instrumentation results (pointer will not remain valid after jit completes) uint32_t * pCountSchemaItems, // pointer to the count schema items uint8_t ** pInstrumentationData, // pointer to the actual instrumentation data (pointer will not remain valid after jit completes) - PgoSource * pPgoSource, + PgoSource * pPgoSource, bool * pDynamicPgo ) { diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index cb8a7ce9f9c24..8a8b345e4c9cf 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -1551,11 +1551,10 @@ class MethodTable inline WORD GetNumIntroducedInstanceFields(); - // Note: This flag MUST be available even from an unrestored MethodTable - see GcScanRoots in siginfo.cpp. - DWORD ContainsPointers() + BOOL ContainsPointers() { LIMITED_METHOD_CONTRACT; - return GetFlag(enum_flag_ContainsPointers); + return !!GetFlag(enum_flag_ContainsPointers); } BOOL Collectible() @@ -3375,32 +3374,24 @@ public : enum_flag_Category_ElementTypeMask = 0x000E0000, // bits that matter for element type mask - - // GC depends on this bit - enum_flag_HasFinalizer = 0x00100000, // instances require finalization - - enum_flag_IDynamicInterfaceCastable = 0x10000000, // class implements IDynamicInterfaceCastable interface - + enum_flag_HasFinalizer = 0x00100000, // instances require finalization. GC depends on this bit. + enum_flag_Collectible = 0x00200000, // GC depends on this bit. enum_flag_ICastable = 0x00400000, // class implements ICastable interface #ifdef FEATURE_64BIT_ALIGNMENT enum_flag_RequiresAlign8 = 0x00800000, // Type requires 8-byte alignment (only set on platforms that require this and don't get it implicitly) #endif - enum_flag_ContainsPointers = 0x01000000, - + enum_flag_ContainsPointers = 0x01000000, // Contains object references enum_flag_HasTypeEquivalence = 0x02000000, // can be equivalent to another type + enum_flag_IsTrackedReferenceWithFinalizer = 0x04000000, + // unused = 0x08000000, - enum_flag_IsTrackedReferenceWithFinalizer = 0x04000000, - - // GC depends on this bit - enum_flag_Collectible = 0x00200000, - enum_flag_ContainsGenericVariables = 0x20000000, // we cache this flag to help detect these efficiently and - // to detect this condition when restoring - + enum_flag_IDynamicInterfaceCastable = 0x10000000, // class implements IDynamicInterfaceCastable interface + enum_flag_ContainsGenericVariables = 0x20000000, // we cache this flag to help detect these efficiently and + // to detect this condition when restoring enum_flag_ComObject = 0x40000000, // class is a com object - - enum_flag_HasComponentSize = 0x80000000, // This is set if component size is used for flags. + enum_flag_HasComponentSize = 0x80000000, // This is set if component size is used for flags. // Types that require non-trivial interface cast have this bit set in the category enum_flag_NonTrivialInterfaceCast = enum_flag_Category_Array diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 69d2ab17c275b..c2c4ec3ddc34b 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -3858,7 +3858,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, FieldDesc * pFD; DWORD dwLog2FieldSize = 0; - BOOL bCurrentFieldIsGCPointer = FALSE; + BOOL bCurrentFieldIsObjectRef = FALSE; mdToken dwByValueClassToken = 0; MethodTable * pByValueClass = NULL; BOOL fIsByValue = FALSE; @@ -4030,7 +4030,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, case ELEMENT_TYPE_OBJECT: { dwLog2FieldSize = LOG2_PTRSIZE; - bCurrentFieldIsGCPointer = TRUE; + bCurrentFieldIsObjectRef = TRUE; FieldDescElementType = ELEMENT_TYPE_CLASS; if (!fIsStatic) @@ -4375,7 +4375,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); else if (IsManagedSequential() && !fIsStatic) IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); - else if (bCurrentFieldIsGCPointer) + else if (bCurrentFieldIsObjectRef) pFD->SetOffset(FIELD_OFFSET_UNPLACED_GC_PTR); else pFD->SetOffset(FIELD_OFFSET_UNPLACED); @@ -4391,7 +4391,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, dwCurrentDeclaredField++; - if (bCurrentFieldIsGCPointer) + if (bCurrentFieldIsObjectRef) { bmtFP->NumInstanceGCPointerFields++; } @@ -4469,7 +4469,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, { bmtFP->NumThreadStaticFieldsOfSize[dwLog2FieldSize]++; - if (bCurrentFieldIsGCPointer) + if (bCurrentFieldIsObjectRef) bmtFP->NumThreadStaticGCPointerFields++; if (fIsByValue) @@ -4479,7 +4479,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, { bmtFP->NumRegularStaticFieldsOfSize[dwLog2FieldSize]++; - if (bCurrentFieldIsGCPointer) + if (bCurrentFieldIsObjectRef) bmtFP->NumRegularStaticGCPointerFields++; if (fIsByValue) diff --git a/src/coreclr/vm/nativelibrary.cpp b/src/coreclr/vm/nativelibrary.cpp index 7acdce8b37df9..953ce5b6fc921 100644 --- a/src/coreclr/vm/nativelibrary.cpp +++ b/src/coreclr/vm/nativelibrary.cpp @@ -597,7 +597,7 @@ namespace int varCount = 0; - // Follow LoadLibrary rules in MSDN doc: https://docs.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya + // Follow LoadLibrary rules in MSDN doc: https://learn.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya // To prevent the function from appending ".DLL" to the module name, include a trailing point character (.) in the module name string // or provide an absolute path. libNameVariations[varCount++] = NameFmt; diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index cf41103441844..c98607ade0a82 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1623,6 +1623,15 @@ BOOL Nullable::IsNullableForTypeHelperNoGC(MethodTable* nullableMT, MethodTable* } //=============================================================================== +int32_t Nullable::GetValueAddrOffset(MethodTable* nullableMT) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(IsNullableType(nullableMT)); + _ASSERTE(strcmp(nullableMT->GetApproxFieldDescListRaw()[1].GetDebugName(), "value") == 0); + return nullableMT->GetApproxFieldDescListRaw()[1].GetOffset(); +} + CLR_BOOL* Nullable::HasValueAddr(MethodTable* nullableMT) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index c486177ee3187..c2bf72c1dee02 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -2473,6 +2473,8 @@ class Nullable { return nullable->ValueAddr(nullableMT); } + static int32_t GetValueAddrOffset(MethodTable* nullableMT); + private: static BOOL IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT); static BOOL IsNullableForTypeHelperNoGC(MethodTable* nullableMT, MethodTable* paramMT); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 685bfba70f501..efff78195d8e3 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1179,17 +1179,19 @@ namespace // Parsing the signature follows details defined in ECMA-335 - II.23.2.1 // + uint32_t callConvDecl; + uint32_t callConvMethod; + IfFailThrow(CorSigUncompressCallingConv(pSig1, cSig1, &callConvDecl)); + IfFailThrow(CorSigUncompressCallingConv(pSig2, cSig2, &callConvMethod)); + pSig1++; + pSig2++; + // Validate calling convention - if ((*pSig1 & IMAGE_CEE_CS_CALLCONV_MASK) != (*pSig2 & IMAGE_CEE_CS_CALLCONV_MASK)) + if ((callConvDecl & IMAGE_CEE_CS_CALLCONV_MASK) != (callConvMethod & IMAGE_CEE_CS_CALLCONV_MASK)) { return false; } - BYTE callConvDecl = *pSig1; - BYTE callConvMethod = *pSig2; - pSig1++; - pSig2++; - // Handle generic param count DWORD declGenericCount = 0; DWORD methodGenericCount = 0; @@ -1402,10 +1404,78 @@ namespace return cxt.TargetMethod != NULL; } + bool DoesFieldMatchUnsafeAccessorDeclaration( + GenerationContext& cxt, + FieldDesc* field, + MetaSig::CompareState& state) + { + STANDARD_VM_CONTRACT; + _ASSERTE(field != NULL); + + PCCOR_SIGNATURE pSig1; + DWORD cSig1; + cxt.Declaration->GetSig(&pSig1, &cSig1); + PCCOR_SIGNATURE pEndSig1 = pSig1 + cSig1; + ModuleBase* pModule1 = cxt.Declaration->GetModule(); + const Substitution* pSubst1 = NULL; + + PCCOR_SIGNATURE pSig2; + DWORD cSig2; + field->GetSig(&pSig2, &cSig2); + PCCOR_SIGNATURE pEndSig2 = pSig2 + cSig2; + ModuleBase* pModule2 = field->GetModule(); + const Substitution* pSubst2 = NULL; + + // + // Parsing the signature follows details defined in ECMA-335 - II.23.2.1 (MethodDefSig) and II.23.2.4 (FieldSig) + // The intent here is to compare the return type in the MethodDefSig with the type in the FieldSig + // + + // Consume calling convention + uint32_t callConvDecl; + uint32_t callConvField; + IfFailThrow(CorSigUncompressCallingConv(pSig1, cSig1, &callConvDecl)); + IfFailThrow(CorSigUncompressCallingConv(pSig2, cSig2, &callConvField)); + _ASSERTE(callConvField == IMAGE_CEE_CS_CALLCONV_FIELD); + pSig1++; + pSig2++; + + // Consume parts of the method signature until we get to the return type. + DWORD declGenericCount = 0; + if (callConvDecl & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailThrow(CorSigUncompressData_EndPtr(pSig1, pEndSig1, &declGenericCount)); + + DWORD declArgCount; + IfFailThrow(CorSigUncompressData_EndPtr(pSig1, pEndSig1, &declArgCount)); + + // UnsafeAccessors for fields require return types be byref. + // This was explicitly checked in TryGenerateUnsafeAccessor(). + if (pSig1 >= pEndSig1) + ThrowHR(META_E_BAD_SIGNATURE); + CorElementType byRefType = CorSigUncompressElementType(pSig1); + _ASSERTE(byRefType == ELEMENT_TYPE_BYREF); + + // Compare the types + if (FALSE == MetaSig::CompareElementType( + pSig1, + pSig2, + pEndSig1, + pEndSig2, + pModule1, + pModule2, + pSubst1, + pSubst2, + &state)) + { + return false; + } + + return true; + } + bool TrySetTargetField( GenerationContext& cxt, - LPCUTF8 fieldName, - TypeHandle fieldType) + LPCUTF8 fieldName) { STANDARD_VM_CONTRACT; _ASSERTE(fieldName != NULL); @@ -1417,7 +1487,6 @@ namespace MethodTable* pMT = targetType.AsMethodTable(); - CorElementType elemType = fieldType.GetSignatureCorElementType(); ApproxFieldDescIterator fdIterator( pMT, (cxt.IsTargetStatic ? ApproxFieldDescIterator::STATIC_FIELDS : ApproxFieldDescIterator::INSTANCE_FIELDS)); @@ -1428,20 +1497,11 @@ namespace if (strcmp(fieldName, pField->GetName()) != 0) continue; - // We check if the possible field is class or valuetype - // since generic fields need resolution. - CorElementType fieldTypeMaybe = pField->GetFieldType(); - if (fieldTypeMaybe == ELEMENT_TYPE_CLASS - || fieldTypeMaybe == ELEMENT_TYPE_VALUETYPE) - { - if (fieldType != pField->LookupFieldTypeHandle()) - continue; - } - else - { - if (elemType != fieldTypeMaybe) - continue; - } + TokenPairList list { nullptr }; + MetaSig::CompareState state{ &list }; + state.IgnoreCustomModifiers = false; + if (!DoesFieldMatchUnsafeAccessorDeclaration(cxt, pField, state)) + continue; if (cxt.Kind == UnsafeAccessorKind::StaticField && pMT->HasGenericsStaticsInfo()) { @@ -1769,7 +1829,7 @@ bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMET context.TargetType = ValidateTargetType(firstArgType, firstArgCorType); context.IsTargetStatic = kind == UnsafeAccessorKind::StaticField; - if (!TrySetTargetField(context, name.GetUTF8(), retType.GetTypeParam())) + if (!TrySetTargetField(context, name.GetUTF8())) MemberLoader::ThrowMissingFieldException(context.TargetType.AsMethodTable(), name.GetUTF8()); break; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 05a62c15df1eb..2ece683380ef5 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -341,6 +341,7 @@ static const Entry s_QCall[] = DllImportEntry(ReflectionInvocation_CompileMethod) DllImportEntry(ReflectionInvocation_PrepareMethod) DllImportEntry(ReflectionInvocation_SizeOf) + DllImportEntry(ReflectionInvocation_GetBoxInfo) DllImportEntry(ReflectionSerialization_GetCreateUninitializedObjectInfo) #if defined(FEATURE_COMWRAPPERS) DllImportEntry(ComWrappers_GetIUnknownImpl) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 65192d5a68738..52c211fe74c8d 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1709,7 +1709,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, - PCODE* ppfnCtor, + PCODE* ppfnRefCtor, + PCODE* ppfnValueCtor, BOOL* pfCtorIsPublic ) { @@ -1718,11 +1719,13 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( QCALL_CHECK; PRECONDITION(CheckPointer(ppfnAllocator)); PRECONDITION(CheckPointer(pvAllocatorFirstArg)); - PRECONDITION(CheckPointer(ppfnCtor)); + PRECONDITION(CheckPointer(ppfnRefCtor)); + PRECONDITION(CheckPointer(ppfnValueCtor)); PRECONDITION(CheckPointer(pfCtorIsPublic)); PRECONDITION(*ppfnAllocator == NULL); PRECONDITION(*pvAllocatorFirstArg == NULL); - PRECONDITION(*ppfnCtor == NULL); + PRECONDITION(*ppfnRefCtor == NULL); + PRECONDITION(*ppfnValueCtor == NULL); PRECONDITION(*pfCtorIsPublic == FALSE); } CONTRACTL_END; @@ -1776,7 +1779,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( // managed sig: ComClassFactory* -> object (via FCALL) *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); *pvAllocatorFirstArg = pClassFactory; - *ppfnCtor = NULL; // no ctor call needed; activation handled entirely by the allocator + *ppfnRefCtor = NULL; // no ctor call needed; activation handled entirely by the allocator + *ppfnValueCtor = NULL; // no value ctor for reference type *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } else @@ -1786,7 +1790,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( // CreateInstance returns null given Nullable *ppfnAllocator = (PCODE)NULL; *pvAllocatorFirstArg = NULL; - *ppfnCtor = (PCODE)NULL; + *ppfnRefCtor = (PCODE)NULL; + *ppfnValueCtor = (PCODE)NULL; *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } else @@ -1796,22 +1801,34 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( *ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT, &fHasSideEffectsUnused)); *pvAllocatorFirstArg = pMT; + BOOL isValueType = pMT->IsValueType(); if (pMT->HasDefaultConstructor()) { // managed sig: object -> void // for ctors on value types, lookup boxed entry point stub - MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() /* forceBoxedEntryPoint */); + MethodDesc* pMD = pMT->GetDefaultConstructor(isValueType /* forceBoxedEntryPoint */); _ASSERTE(pMD != NULL); PCODE pCode = pMD->GetMultiCallableAddrOfCode(); _ASSERTE(pCode != (PCODE)NULL); - *ppfnCtor = pCode; + *ppfnRefCtor = pCode; *pfCtorIsPublic = pMD->IsPublic(); + + // If we have a value type, get the non-boxing entry point too. + if (isValueType) + { + pMD = pMT->GetDefaultConstructor(FALSE /* forceBoxedEntryPoint */); + _ASSERTE(pMD != NULL); + pCode = pMD->GetMultiCallableAddrOfCode(); + _ASSERTE(pCode != (PCODE)NULL); + *ppfnValueCtor = pCode; + } } - else if (pMT->IsValueType()) + else if (isValueType) { - *ppfnCtor = (PCODE)NULL; // no ctor call needed; we're creating a boxed default(T) + *ppfnRefCtor = (PCODE)NULL; // no ctor call needed; we're creating a boxed default(T) + *ppfnValueCtor = (PCODE)NULL; *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } else @@ -2067,3 +2084,49 @@ extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType return handle.GetSize(); } + +extern "C" void QCALLTYPE ReflectionInvocation_GetBoxInfo( + QCall::TypeHandle pType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg, + int32_t* pValueOffset, + uint32_t* pValueSize) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(CheckPointer(ppfnAllocator)); + PRECONDITION(CheckPointer(pvAllocatorFirstArg)); + PRECONDITION(*ppfnAllocator == NULL); + PRECONDITION(*pvAllocatorFirstArg == NULL); + } + CONTRACTL_END; + + BEGIN_QCALL; + + TypeHandle type = pType.AsTypeHandle(); + + RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */); + + MethodTable* pMT = type.AsMethodTable(); + + _ASSERTE(pMT->IsValueType() || pMT->IsNullable() || pMT->IsEnum() || pMT->IsTruePrimitive()); + + *pValueOffset = 0; + + // If it is a nullable, return the allocator for the underlying type instead. + if (pMT->IsNullable()) + { + *pValueOffset = Nullable::GetValueAddrOffset(pMT); + pMT = pMT->GetInstantiation()[0].GetMethodTable(); + } + + bool fHasSideEffectsUnused; + *ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT, &fHasSideEffectsUnused)); + *pvAllocatorFirstArg = pMT; + *pValueSize = pMT->GetNumInstanceFieldBytes(); + + pMT->EnsureInstanceActive(); + + END_QCALL; +} \ No newline at end of file diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index 3bbf1d57bb1ed..c26f137cdd927 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -72,6 +72,13 @@ extern "C" void QCALLTYPE ReflectionInvocation_PrepareMethod(MethodDesc* pMD, Ty extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectInfo(QCall::TypeHandle pType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg); +extern "C" void QCALLTYPE ReflectionInvocation_GetBoxInfo( + QCall::TypeHandle pType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg, + int32_t* pValueOffset, + uint32_t* pValueSize); + class ReflectionEnum { public: static FCDECL1(INT32, InternalGetCorElementType, MethodTable* pMT); diff --git a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp index 8372d5f0bc4c4..2f39e4f313c58 100644 --- a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp @@ -156,9 +156,9 @@ struct ResolveStub private: friend struct ResolveHolder; - const static int resolveEntryPointLen = 20; - const static int slowEntryPointLen = 4; - const static int failEntryPointLen = 9; + constexpr static int resolveEntryPointLen = 20; + constexpr static int slowEntryPointLen = 4; + constexpr static int failEntryPointLen = 9; DWORD _resolveEntryPoint[resolveEntryPointLen]; DWORD _slowEntryPoint[slowEntryPointLen]; @@ -226,10 +226,12 @@ struct ResolveHolder // addi t0, t0, -12 _stub._resolveEntryPoint[n++] = 0xff428293; + constexpr size_t entryPointsLen = ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen; + constexpr size_t hashedTokenOffset = offsetof(ResolveStub, _hashedToken); // lw t6, 0(t0) #t6 = this._hashedToken - _stub._resolveEntryPoint[n++] = 0x0002af83 | (33 << 22); //(20+4+9)*4<<20; - _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen) == 33); - _ASSERTE((33<<2) == (offsetof(ResolveStub, _hashedToken) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + _stub._resolveEntryPoint[n++] = 0x0002af83 | (hashedTokenOffset << 20); + static_assert_no_msg(entryPointsLen << 2 == hashedTokenOffset); + static_assert_no_msg(offsetof(ResolveStub, _resolveEntryPoint[0]) == 0); // xor t1, t1, t6 _stub._resolveEntryPoint[n++] = 0x01f34333; @@ -241,10 +243,10 @@ struct ResolveHolder _stub._resolveEntryPoint[n++] = 0x00cfdf9b; // and t1, t1, t6 _stub._resolveEntryPoint[n++] = 0x01f37333; + constexpr size_t cacheAddressOffset = offsetof(ResolveStub, _cacheAddress); // ld t6, 0(t0) # t6 = this._cacheAddress - _stub._resolveEntryPoint[n++] = 0x0002bf83 | (36 << 22); //(20+4+9+1+2)*4<<20; - _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+2) == 36); - _ASSERTE((36<<2) == (offsetof(ResolveStub, _cacheAddress) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + _stub._resolveEntryPoint[n++] = 0x0002bf83 | (cacheAddressOffset << 20); + static_assert_no_msg((entryPointsLen+1+2) << 2 == cacheAddressOffset); // add t1, t6, t1 _stub._resolveEntryPoint[n++] = 0x006f8333; // ld t1, 0(t1) # t1 = e = this._cacheAddress[i] @@ -252,10 +254,10 @@ struct ResolveHolder // ld t6, 0(t1) # t6 = Check mt == e.pMT; _stub._resolveEntryPoint[n++] = 0x00033f83 | ((offsetof(ResolveCacheElem, pMT) & 0xfff) << 20); + constexpr size_t tokenOffset = offsetof(ResolveStub, _token); // ld t2, 0(t0) # $t2 = this._token - _stub._resolveEntryPoint[n++] = 0x0002b383 | (38<<22);//(20+4+9+1+2+2)*4<<20; - _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4) == 38); - _ASSERTE((38<<2) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + _stub._resolveEntryPoint[n++] = 0x0002b383 | (tokenOffset << 20); + static_assert_no_msg((entryPointsLen+1+4) << 2 == tokenOffset); // bne t6, t3, next _stub._resolveEntryPoint[n++] = 0x01cf9a63;// | PC_REL_OFFSET(_slowEntryPoint[0], n); @@ -288,19 +290,19 @@ struct ResolveHolder // auipc t0, 0 _stub._slowEntryPoint[0] = 0x00000297; // ld t6, 0(t0) # r21 = _resolveWorkerTarget; - _ASSERTE((0x14*4) == ((INT32)(offsetof(ResolveStub, _resolveWorkerTarget) - (offsetof(ResolveStub, _slowEntryPoint[0]))))); - _ASSERTE((ResolveStub::slowEntryPointLen + ResolveStub::failEntryPointLen+1+3*2) == 0x14); + static_assert_no_msg((0x14*4) == ((INT32)(offsetof(ResolveStub, _resolveWorkerTarget) - (offsetof(ResolveStub, _slowEntryPoint[0]))))); + static_assert_no_msg((ResolveStub::slowEntryPointLen + ResolveStub::failEntryPointLen+1+3*2) == 0x14); _stub._slowEntryPoint[1] = 0x0002bf83 | ((0x14 * 4) << 20); // ld t2, 0(t0) # t2 = this._token; _stub._slowEntryPoint[2] = 0x0002b383 | ((0x12 * 4) << 20); //(18*4=72=0x48)<<20 - _ASSERTE((ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4)*4 == (0x12 * 4)); - _ASSERTE((0x12 * 4) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _slowEntryPoint[0]))); + static_assert_no_msg((ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4)*4 == (0x12 * 4)); + static_assert_no_msg((0x12 * 4) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _slowEntryPoint[0]))); // jalr x0, t6, 0 _stub._slowEntryPoint[3] = 0x000f8067; - _ASSERTE(4 == ResolveStub::slowEntryPointLen); + static_assert_no_msg(4 == ResolveStub::slowEntryPointLen); // ResolveStub._failEntryPoint(a0:MethodToken, a1,.., a7, t5:IndirectionCellAndFlags) // { @@ -315,8 +317,8 @@ struct ResolveHolder _stub._failEntryPoint[0] = 0x00000297; // ld t1, 0(t0) # t1 = _pCounter; 0x2800000=((failEntryPointLen+1)*4)<<20. _stub._failEntryPoint[1] = 0x0002b303 | 0x2800000; - _ASSERTE((((ResolveStub::failEntryPointLen+1)*4)<<20) == 0x2800000); - _ASSERTE((0x2800000>>20) == ((INT32)(offsetof(ResolveStub, _pCounter) - (offsetof(ResolveStub, _failEntryPoint[0]))))); + static_assert_no_msg((((ResolveStub::failEntryPointLen+1)*4)<<20) == 0x2800000); + static_assert_no_msg((0x2800000>>20) == ((INT32)(offsetof(ResolveStub, _pCounter) - (offsetof(ResolveStub, _failEntryPoint[0]))))); // lw t6, 0(t1) _stub._failEntryPoint[2] = 0x00032f83; // addi t6, t6, -1 @@ -325,7 +327,7 @@ struct ResolveHolder // sw t6, 0(t1) _stub._failEntryPoint[4] = 0x01f32023; - _ASSERTE(SDF_ResolveBackPatch == 0x1); + static_assert_no_msg(SDF_ResolveBackPatch == 0x1); // ;; ori t5, t5, t6 >=0 ? SDF_ResolveBackPatch:0; // slti t6, t6, 0 _stub._failEntryPoint[5] = 0x000faf93; @@ -337,8 +339,8 @@ struct ResolveHolder // j _resolveEntryPoint // pc - 128 = pc + 4 - resolveEntryPointLen * 4 - slowEntryPointLen * 4 - failEntryPointLen * 4; _stub._failEntryPoint[8] = 0xf81ff06f; - _ASSERTE(9 == ResolveStub::failEntryPointLen); - _stub._pCounter = counterAddr; + static_assert_no_msg(9 == ResolveStub::failEntryPointLen); + _stub._pCounter = counterAddr; _stub._hashedToken = hashedToken << LOG2_PTRSIZE; _stub._cacheAddress = (size_t) cacheAddr; _stub._token = dispatchToken; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index e0504d952880d..a444636618bc1 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -179,6 +179,7 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( PCODE* ppfnAllocator, void** pvAllocatorFirstArg, PCODE* ppfnCtor, + PCODE* ppfnValueCtor, BOOL* pfCtorIsPublic); extern "C" void QCALLTYPE RuntimeTypeHandle_MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); extern "C" void QCALLTYPE RuntimeTypeHandle_MakePointer(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); diff --git a/src/coreclr/vm/syncblk.cpp b/src/coreclr/vm/syncblk.cpp index 482b9da36644f..868fffca0c055 100644 --- a/src/coreclr/vm/syncblk.cpp +++ b/src/coreclr/vm/syncblk.cpp @@ -2097,7 +2097,7 @@ BOOL ObjHeader::Validate (BOOL bVerifySyncBlkIndex) // Warning: Assumes you already own the cache lock. // Assumes nothing allocated inside the SyncBlock (only releases the memory, does not destruct.) // -// This holder really just meets GetSyncBlock()'s special needs. It's not a general purpose holder. +// This holder really just meets GetSyncBlock()'s special requirements. It's not a general purpose holder. // Do not inline this call. (fyuan) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 7411d62a285f8..68e2d80935c40 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2038,7 +2038,6 @@ BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, voi return bRet; #endif // !TARGET_UNIX - m_StateNC = (ThreadStateNoConcurrency)((ULONG)m_StateNC | TSNC_CLRCreatedThread); bRet = CreateNewOSThread(stackSize, start, args); #ifndef TARGET_UNIX UndoRevert(bReverted, token); @@ -3843,9 +3842,6 @@ DWORD Thread::DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE (ARG_SLOT)millis, }; - // Needed by TriggerGCForMDAInternal to avoid infinite recursion - ThreadStateNCStackHolder holder(TRUE, TSNC_InsideSyncContextWait); - return invokeWaitMethodHelper.Call_RetI4(args); } @@ -8170,12 +8166,15 @@ void Thread::InitializeSpecialUserModeApc() EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); #endif -void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) +void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord, size_t targetSSP) { #if defined(TARGET_AMD64) - DWORD64 ssp = GetSSP(ContextRecord); + if (targetSSP == 0) + { + targetSSP = GetSSP(ContextRecord); + } __asan_handle_no_return(); - ClrRestoreNonvolatileContextWorker(ContextRecord, ssp); + ClrRestoreNonvolatileContextWorker(ContextRecord, targetSSP); #else __asan_handle_no_return(); // Falling back to RtlRestoreContext() for now, though it should be possible to have simpler variants for these cases diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index c84fd8a4dec3b..1ae521b87d54b 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -537,8 +537,6 @@ class Thread friend BOOL NTGetThreadContext(Thread *pThread, T_CONTEXT *pContext); friend BOOL NTSetThreadContext(Thread *pThread, const T_CONTEXT *pContext); - friend void CommonTripThread(); - #ifdef FEATURE_HIJACK // MapWin32FaultToCOMPlusException needs access to Thread::IsAddrOfRedirectFunc() friend DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord); @@ -552,8 +550,6 @@ class Thread friend void InitThreadManager(); - friend void CallFinalizerOnThreadObject(Object *obj); - // Debug and Profiler caches ThreadHandle. friend class Debugger; // void Debugger::ThreadStarted(Thread* pRuntimeThread, BOOL fAttaching); #if defined(DACCESS_COMPILE) @@ -691,7 +687,7 @@ class Thread // unused = 0x00000010, TSNC_BlockedForShutdown = 0x00000020, // Thread is blocked in WaitForEndOfShutdown. We should not hit WaitForEndOfShutdown again. // unused = 0x00000040, - TSNC_CLRCreatedThread = 0x00000080, // The thread was created through Thread::CreateNewThread + // unused = 0x00000080, TSNC_ExistInThreadStore = 0x00000100, // For dtor to know if it needs to be removed from ThreadStore // unused = 0x00000200, TSNC_OwnsSpinLock = 0x00000400, // The thread owns a spinlock. @@ -706,11 +702,10 @@ class Thread // at the beginning of wait. // unused = 0x00040000, // unused = 0x00080000, - TSNC_RaiseUnloadEvent = 0x00100000, // Finalize thread is raising managed unload event which - // may call AppDomain.Unload. + // unused = 0x00100000, // unused = 0x00200000, // unused = 0x00400000, - TSNC_IgnoreUnhandledExceptions = 0x00800000, // Set for a managed thread born inside an appdomain created with the APPDOMAIN_IGNORE_UNHANDLED_EXCEPTIONS flag. + // unused = 0x00800000, TSNC_ProcessedUnhandledException = 0x01000000,// Set on a thread on which we have done unhandled exception processing so that // we dont perform it again when OS invokes our UEF. Currently, applicable threads include: // 1) entry point thread of a managed app @@ -722,7 +717,7 @@ class Thread // effort. // // Once we are completely independent of the OS UEF, we could remove this. - TSNC_InsideSyncContextWait = 0x02000000, // Whether we are inside DoSyncContextWait + // unused = 0x02000000, TSNC_DebuggerSleepWaitJoin = 0x04000000, // Indicates to the debugger that this thread is in a sleep wait or join state // This almost mirrors the TS_Interruptible state however that flag can change // during GC-preemptive mode whereas this one cannot. @@ -4042,7 +4037,10 @@ class Thread template<> struct cdac_offsets { + static constexpr size_t Id = offsetof(Thread, m_ThreadId); + static constexpr size_t OSId = offsetof(Thread, m_OSThreadId); static constexpr size_t ExposedObject = offsetof(Thread, m_ExposedObject); + static constexpr size_t LastThrownObject = offsetof(Thread, m_LastThrownObjectHandle); static constexpr size_t Link = offsetof(Thread, m_Link); }; @@ -4305,8 +4303,12 @@ class ThreadStore template<> struct cdac_offsets { - static constexpr size_t ThreadList = offsetof(ThreadStore, m_ThreadList); + static constexpr size_t FirstThreadLink = offsetof(ThreadStore, m_ThreadList) + offsetof(ThreadList, m_link); static constexpr size_t ThreadCount = offsetof(ThreadStore, m_ThreadCount); + static constexpr size_t UnstartedCount = offsetof(ThreadStore, m_UnstartedThreadCount); + static constexpr size_t BackgroundCount = offsetof(ThreadStore, m_BackgroundThreadCount); + static constexpr size_t PendingCount = offsetof(ThreadStore, m_PendingThreadCount); + static constexpr size_t DeadCount = offsetof(ThreadStore, m_DeadThreadCount); }; struct TSSuspendHelper { @@ -5769,7 +5771,7 @@ class StackWalkerWalkingThreadHolder #if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); #endif -void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord); +void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord, size_t targetSSP = 0); #endif // DACCESS_COMPILE #endif //__threads_h__ diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 74d2eb08a3dc8..aa9408a7791a6 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -52,13 +52,22 @@ void ThreadLocalBlock::FreeTLM(SIZE_T i, BOOL isThreadShuttingdown) ThreadLocalModule::CollectibleDynamicEntry *entry = (ThreadLocalModule::CollectibleDynamicEntry*)pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry; PTR_LoaderAllocator pLoaderAllocator = entry->m_pLoaderAllocator; - if (entry->m_hGCStatics != 0) - { - pLoaderAllocator->FreeHandle(entry->m_hGCStatics); - } - if (entry->m_hNonGCStatics != 0) + // LoaderAllocator may be collected when the thread is shutting down. + // We enter coop mode to ensure that we get a valid value of the exposed object and + // can safely clean up handles if it is not yet collected. + GCX_COOP(); + + LOADERALLOCATORREF loaderAllocator = pLoaderAllocator->GetExposedObject(); + if (loaderAllocator != NULL) { - pLoaderAllocator->FreeHandle(entry->m_hNonGCStatics); + if (entry->m_hGCStatics != 0) + { + pLoaderAllocator->FreeHandle(entry->m_hGCStatics); + } + if (entry->m_hNonGCStatics != 0) + { + pLoaderAllocator->FreeHandle(entry->m_hNonGCStatics); + } } } delete pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry; diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index 1acecf307c842..8abeb3972d38a 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -601,6 +601,15 @@ class TypeHandlePairList // TODO: Template for TypeHandleList, TypeHandlePairLis }; #if CHECK_INVARIANTS +template +typename std::enable_if::value, CHECK>::type CheckPointerImpl(Dummy th, IsNullOK ok) +{ + CHECK(th.Check()); +} + +template +typename std::enable_if::value, CHECK>::type CheckPointerImpl(Dummy th, IsNullOK ok) { CHECK_OK; } + inline CHECK CheckPointer(TypeHandle th, IsNullOK ok = NULL_NOT_OK) { STATIC_CONTRACT_NOTHROW; @@ -615,10 +624,7 @@ inline CHECK CheckPointer(TypeHandle th, IsNullOK ok = NULL_NOT_OK) } else { - __if_exists(TypeHandle::Check) - { - CHECK(th.Check()); - } + CheckPointerImpl(th, ok); #if 0 CHECK(CheckInvariant(o)); #endif @@ -626,7 +632,6 @@ inline CHECK CheckPointer(TypeHandle th, IsNullOK ok = NULL_NOT_OK) CHECK_OK; } - #endif // CHECK_INVARIANTS /*************************************************************************/ diff --git a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props index f1f6193300baf..c6010482efa8a 100644 --- a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props +++ b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props @@ -1,7 +1,7 @@ + https://github.com/dotnet/sdk/blob/main/src/Installer/redist-installer/targets/GenerateBundledVersions.targets --> diff --git a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj index 33d4b7ed137f2..c6156a1ee35fd 100644 --- a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj +++ b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj @@ -2,7 +2,6 @@ true - true Provides a native AOT compiler and runtime for .NET @@ -16,6 +15,7 @@ + @@ -28,6 +28,18 @@ + + + + + + + + + + + + diff --git a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs index 66c0a2c9763c9..4cd7a495e9401 100644 --- a/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs +++ b/src/installer/tests/HostActivation.Tests/RegisteredInstallLocationOverride.cs @@ -36,7 +36,7 @@ public RegisteredInstallLocationOverride(string productBinaryPath) // product must look into the 32-bit hive. // Without the redirection we would not be able to test that the product always looks // into 32-bit only. - // Per this page https://docs.microsoft.com/en-us/windows/desktop/WinProg64/shared-registry-keys + // Per this page https://learn.microsoft.com/windows/desktop/WinProg64/shared-registry-keys // a user writable redirected key is for example HKCU\Software\Classes\Interface // so we're going to use that one - it's not super clean as the key stores COM interfaces, // but we should not corrupt anything by adding a special subkey even if it's left behind. diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs index b125a8553cd41..901e899bf26c3 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs @@ -26,10 +26,10 @@ public class CreateAppHost [Fact] public void EmbedAppBinaryPath() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; HostWriter.CreateAppHost( @@ -55,14 +55,14 @@ public void EmbedAppBinaryPath() [Fact] public void PlaceholderHashNotFound_Fails() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content => + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location, content => { // Corrupt the hash value content[WindowsFileHeader.Length + 1]++; }); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; Assert.Throws(() => @@ -78,10 +78,10 @@ public void PlaceholderHashNotFound_Fails() [Fact] public void AppBinaryPathTooLong_Fails() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = new string('a', 1024 + 5); Assert.Throws(() => @@ -97,10 +97,10 @@ public void AppBinaryPathTooLong_Fails() [Fact] public void GUISubsystem_WindowsPEFile() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; HostWriter.CreateAppHost( @@ -121,15 +121,15 @@ public void GUISubsystem_WindowsPEFile() [Fact] public void GUISubsystem_NonWindowsPEFile_Fails() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content => + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location, content => { // Windows PE files must start with 0x5A4D, so write some other value here. content[0] = 1; content[1] = 2; }); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; Assert.Throws(() => @@ -146,14 +146,14 @@ public void GUISubsystem_NonWindowsPEFile_Fails() [Fact] public void GUISubsystem_WrongDefault_Fails() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content => + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location, content => { // Corrupt the value of the subsystem (the default should be 3) content[SubsystemOffset] = 42; }); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; Assert.Equal(42, PEUtils.GetWindowsGraphicalUserInterfaceBit(sourceAppHostMock)); @@ -172,9 +172,9 @@ public void GUISubsystem_WrongDefault_Fails() [PlatformSpecific(TestPlatforms.AnyUnix)] public void ExecutableImage() { - using TestDirectory testDirectory = TestDirectory.Create(); - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + using TestArtifact artifact = CreateTestDirectory(); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; // strip executable permissions from this AppHost template binary @@ -204,11 +204,13 @@ public void ExecutableImage() [InlineData("dir with spaces")] public void CodeSignAppHostOnMacOS(string subdir) { - using (TestDirectory testDirectory = TestDirectory.Create(subdir)) + using (TestArtifact artifact = CreateTestDirectory()) { + string testDirectory = Path.Combine(artifact.Location, subdir); + Directory.CreateDirectory(testDirectory); string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(testDirectory, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; HostWriter.CreateAppHost( sourceAppHostMock, @@ -229,7 +231,7 @@ public void CodeSignAppHostOnMacOS(string subdir) { p.Start(); p.StandardError.ReadToEnd() - .Should().Contain($"Executable=/private{Path.GetFullPath(destinationFilePath)}"); + .Should().Contain($"Executable={Path.GetFullPath(destinationFilePath)}"); p.WaitForExit(); // Successfully signed the apphost. Assert.True(p.ExitCode == 0, $"Expected exit code was '0' but '{codesign}' returned '{p.ExitCode}' instead."); @@ -241,11 +243,11 @@ public void CodeSignAppHostOnMacOS(string subdir) [PlatformSpecific(TestPlatforms.OSX)] public void DoesNotCodeSignAppHostByDefault() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; HostWriter.CreateAppHost( sourceAppHostMock, @@ -275,11 +277,11 @@ public void DoesNotCodeSignAppHostByDefault() [PlatformSpecific(TestPlatforms.OSX)] public void CodeSigningFailuresThrow() { - using (TestDirectory testDirectory = TestDirectory.Create()) + using (TestArtifact artifact = CreateTestDirectory()) { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); + string sourceAppHostMock = PrepareAppHostMockFile(artifact.Location); File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); + string destinationFilePath = Path.Combine(artifact.Location, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; HostWriter.CreateAppHost( sourceAppHostMock, @@ -312,7 +314,7 @@ private void ResourceWithUnknownLanguage() } } - private string PrepareAppHostMockFile(TestDirectory testDirectory, Action customize = null) + private string PrepareAppHostMockFile(string directory, Action customize = null) { // For now we're testing the AppHost on Windows PE files only. // The only customization which we do on non-Windows files is the embedding @@ -325,7 +327,7 @@ private string PrepareAppHostMockFile(TestDirectory testDirectory, Action TestArtifact.Create(callingMethod); } } diff --git a/src/installer/tests/TestUtils/ArgumentEscaper.cs b/src/installer/tests/TestUtils/ArgumentEscaper.cs index f805ec8a69622..1c8dc3d25eb56 100644 --- a/src/installer/tests/TestUtils/ArgumentEscaper.cs +++ b/src/installer/tests/TestUtils/ArgumentEscaper.cs @@ -14,7 +14,7 @@ public static class ArgumentEscaper /// so that the next process will receive the same string[] args /// /// See here for more info: - /// https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way + /// https://learn.microsoft.com/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way /// /// /// @@ -28,7 +28,7 @@ public static string EscapeAndConcatenateArgArrayForProcessStart(IEnumerable /// /// @@ -99,7 +99,7 @@ private static string EscapeArg(string arg) /// be to do this only for cmd metacharacters. /// /// See here for more info: - /// https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way + /// https://learn.microsoft.com/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way /// /// /// diff --git a/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs b/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs new file mode 100644 index 0000000000000..ec54fb705a836 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Swift.Runtime +{ + // + // Represents Swift UnsafeBufferPointer in C#. + // + internal readonly unsafe struct UnsafeBufferPointer where T : unmanaged + { + private readonly T* _baseAddress; + private readonly nint _count; + public UnsafeBufferPointer(T* baseAddress, nint count) + { + _baseAddress = baseAddress; + _count = count; + } + + public T* BaseAddress => _baseAddress; + public nint Count => _count; + } + + // + // Represents Swift UnsafeMutableBufferPointer in C#. + // + internal readonly unsafe struct UnsafeMutableBufferPointer where T : unmanaged + { + private readonly T* _baseAddress; + private readonly nint _count; + public UnsafeMutableBufferPointer(T* baseAddress, nint count) + { + _baseAddress = baseAddress; + _count = count; + } + + public T* BaseAddress => _baseAddress; + public nint Count => _count; + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs index 2f405e0bfd0da..3e7583694b1ca 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs @@ -5,8 +5,10 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; using System.Security.Cryptography; using System.Security.Cryptography.Apple; +using Swift.Runtime; #pragma warning disable CS3016 // Arrays as attribute arguments are not CLS Compliant @@ -14,6 +16,17 @@ internal static partial class Interop { internal static partial class AppleCrypto { + private static byte NullSentinel; + + // CryptoKit doesn't do well with a null pointer for the buffer data, + // so provide a sentinel pointer instead. + private static ref readonly byte GetSwiftRef(ReadOnlySpan b) + { + return ref (b.Length == 0 + ? ref NullSentinel + : ref MemoryMarshal.GetReference(b)); + } + internal static unsafe void ChaCha20Poly1305Encrypt( ReadOnlySpan key, ReadOnlySpan nonce, @@ -24,23 +37,22 @@ internal static unsafe void ChaCha20Poly1305Encrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* aadPtr = aad) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - int result = AppleCryptoNative_ChaCha20Poly1305Encrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - plaintextPtr, plaintext.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_ChaCha20Poly1305Encrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeMutableBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeMutableBufferPointer(tagPtr, tag.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { - Debug.Assert(result == 0); CryptographicOperations.ZeroMemory(ciphertext); CryptographicOperations.ZeroMemory(tag); throw new CryptographicException(); @@ -58,32 +70,30 @@ internal static unsafe void ChaCha20Poly1305Decrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* aadPtr = aad) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - const int AuthTagMismatch = -1; - int result = AppleCryptoNative_ChaCha20Poly1305Decrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - plaintextPtr, plaintext.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_ChaCha20Poly1305Decrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeBufferPointer(tagPtr, tag.Length), + new UnsafeMutableBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { CryptographicOperations.ZeroMemory(plaintext); - if (result == AuthTagMismatch) + if (AppleCryptoNative_IsAuthenticationFailure(error.Value)) { throw new AuthenticationTagMismatchException(); } else { - Debug.Assert(result == 0); throw new CryptographicException(); } } @@ -100,23 +110,22 @@ internal static unsafe void AesGcmEncrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* aadPtr = aad) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - int result = AppleCryptoNative_AesGcmEncrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - plaintextPtr, plaintext.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_AesGcmEncrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeMutableBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeMutableBufferPointer(tagPtr, tag.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { - Debug.Assert(result == 0); CryptographicOperations.ZeroMemory(ciphertext); CryptographicOperations.ZeroMemory(tag); throw new CryptographicException(); @@ -134,32 +143,30 @@ internal static unsafe void AesGcmDecrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* aadPtr = aad) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - const int AuthTagMismatch = -1; - int result = AppleCryptoNative_AesGcmDecrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - plaintextPtr, plaintext.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_AesGcmDecrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeBufferPointer(tagPtr, tag.Length), + new UnsafeMutableBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { CryptographicOperations.ZeroMemory(plaintext); - if (result == AuthTagMismatch) + if (AppleCryptoNative_IsAuthenticationFailure(error.Value)) { throw new AuthenticationTagMismatchException(); } else { - Debug.Assert(result == 0); throw new CryptographicException(); } } @@ -168,66 +175,51 @@ internal static unsafe void AesGcmDecrypt( [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_ChaCha20Poly1305Encrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* plaintextPtr, - int plaintextLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_ChaCha20Poly1305Encrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer plaintext, + UnsafeMutableBufferPointer ciphertext, + UnsafeMutableBufferPointer tag, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_ChaCha20Poly1305Decrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* plaintextPtr, - int plaintextLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_ChaCha20Poly1305Decrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer ciphertext, + UnsafeBufferPointer tag, + UnsafeMutableBufferPointer plaintext, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_AesGcmEncrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* plaintextPtr, - int plaintextLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_AesGcmEncrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer plaintext, + UnsafeMutableBufferPointer ciphertext, + UnsafeMutableBufferPointer tag, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_AesGcmDecrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* plaintextPtr, - int plaintextLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_AesGcmDecrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer ciphertext, + UnsafeBufferPointer tag, + UnsafeMutableBufferPointer plaintext, + UnsafeBufferPointer aad); + + [LibraryImport(Libraries.AppleCryptoNative)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvSwift) })] + [return: MarshalAs(UnmanagedType.U1)] + private static unsafe partial bool AppleCryptoNative_IsAuthenticationFailure(void* error); } } diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs index a21197310f955..237c5c214b027 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs @@ -3,7 +3,7 @@ internal static partial class Interop { - // As defined in winerror.h and https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes + // As defined in winerror.h and https://learn.microsoft.com/windows/win32/debug/system-error-codes internal static partial class Errors { internal const int ERROR_SUCCESS = 0x0; diff --git a/src/libraries/Common/src/Interop/Windows/Interop.SECURITY_QUALITY_OF_SERVICE.cs b/src/libraries/Common/src/Interop/Windows/Interop.SECURITY_QUALITY_OF_SERVICE.cs index 3b63485ef987e..6a0f0d4795c43 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.SECURITY_QUALITY_OF_SERVICE.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.SECURITY_QUALITY_OF_SERVICE.cs @@ -4,7 +4,7 @@ internal static partial class Interop { /// - /// SECURITY_QUALITY_OF_SERVICE structure. + /// SECURITY_QUALITY_OF_SERVICE structure. /// Used to support client impersonation. Client specifies this to a server to allow /// it to impersonate the client. /// diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateToolhelp32Snapshot.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateToolhelp32Snapshot.cs index 4a0d3d50686e1..6dd27a9fb4e7a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateToolhelp32Snapshot.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateToolhelp32Snapshot.cs @@ -36,16 +36,16 @@ internal unsafe struct PROCESSENTRY32 internal fixed char szExeFile[MAX_PATH]; } - // https://docs.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot + // https://learn.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot [LibraryImport(Libraries.Kernel32, SetLastError = true)] internal static partial nint CreateToolhelp32Snapshot(SnapshotFlags dwFlags, uint th32ProcessID); - // https://docs.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-process32first + // https://learn.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-process32first [LibraryImport(Libraries.Kernel32, EntryPoint = "Process32FirstW", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool Process32First(nint hSnapshot, PROCESSENTRY32* lppe); - // https://docs.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-process32next + // https://learn.microsoft.com/windows/desktop/api/tlhelp32/nf-tlhelp32-process32next [LibraryImport(Libraries.Kernel32, EntryPoint = "Process32NextW", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool Process32Next(nint hSnapshot, PROCESSENTRY32* lppe); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs index aae40b9bb7fdd..05e16899ba2b4 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs @@ -9,10 +9,10 @@ internal static partial class Interop { internal static partial class Kernel32 { - // https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point + // https://learn.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8; - // https://docs.microsoft.com/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_read_capacity + // https://learn.microsoft.com/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_read_capacity internal const int IOCTL_STORAGE_READ_CAPACITY = 0x002D5140; [LibraryImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs index 0233626ee73c6..a47c89dc8a5e2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs @@ -5,7 +5,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - // Value taken from https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle#remarks: + // Value taken from https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle#remarks: internal const int FileAllocationInfo = 5; internal struct FILE_ALLOCATION_INFO diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs index cdc0fcc3e5319..4b4ac6c806f1c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs @@ -12,7 +12,7 @@ internal static partial class Kernel32 { internal const uint FILE_NAME_NORMALIZED = 0x0; - // https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew (kernel32) + // https://learn.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew (kernel32) [LibraryImport(Libraries.Kernel32, EntryPoint = "GetFinalPathNameByHandleW", SetLastError = true)] internal static unsafe partial uint GetFinalPathNameByHandle( SafeFileHandle hFile, diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs index be477375c81a3..a565e1b55c045 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Kernel32 { /// - /// RequiredAccess. + /// RequiredAccess. /// Indicates the type of access that a caller must request when opening the file object that represents the device (see IRP_MJ_CREATE). /// [Flags] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs index 123ac9235b9fd..dbfc7c33d7e50 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - // https://docs.microsoft.com/windows-hardware/drivers/ifs/fsctl-get-reparse-point + // https://learn.microsoft.com/windows-hardware/drivers/ifs/fsctl-get-reparse-point internal const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; internal const uint SYMLINK_FLAG_RELATIVE = 1; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs index 49d3af39f9b48..cb6913fa9d114 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.STORAGE_READ_CAPACITY.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - // https://docs.microsoft.com/en-us/windows/win32/devio/storage-read-capacity + // https://learn.microsoft.com/windows/win32/devio/storage-read-capacity [StructLayout(LayoutKind.Sequential)] internal unsafe struct STORAGE_READ_CAPACITY { diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs index 24f807538bf59..29bfaeed5f703 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class NtDll { /// - /// FILE_FULL_EA_INFORMATION structure. + /// FILE_FULL_EA_INFORMATION structure. /// Provides extended attribute (EA) information. This structure is used primarily by network drivers. /// [StructLayoutAttribute(LayoutKind.Sequential)] diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs index ad48e8e74d52a..e2a62a36cdf23 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs @@ -6,7 +6,7 @@ internal static partial class Interop internal static class StatusOptions { // See the NT_SUCCESS macro in the Windows SDK, and - // https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values + // https://learn.microsoft.com/windows-hardware/drivers/kernel/using-ntstatus-values internal static bool NT_SUCCESS(uint ntStatus) => (int)ntStatus >= 0; // Error codes from ntstatus.h diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs index e922be5089973..181d4a9ca0299 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Ole32 { /// - /// IStream interface. + /// IStream interface. /// /// /// This interface explicitly doesn't use the built-in COM support, but instead is only used with ComWrappers. diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs index 0dee8c4ae3193..a14f144e68955 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.IStream.NoCOMWrappers.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Ole32 { /// - /// COM IStream interface. + /// COM IStream interface. /// /// /// The definition in does not lend diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs index 38cd9844524c1..169ff3cb00924 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs @@ -7,7 +7,7 @@ internal static partial class Ole32 { /// /// Stat flags for . - /// + /// /// internal enum STATFLAG : uint { diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs index 6e1cf2c7e8613..7cccfcf75ce9a 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs @@ -11,7 +11,7 @@ internal static partial class Ole32 { /// /// Statistics for . - /// + /// /// /// /// The definition in isn't blittable. @@ -41,7 +41,7 @@ internal struct STATSTG /// /// Supported locking modes. - /// + /// /// /// /// '0' means does not support lock modes. diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGM.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGM.cs index a946ed209d428..815fa63bfca4b 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGM.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGM.cs @@ -9,7 +9,7 @@ internal static partial class Ole32 { /// /// Stream / storage modes. - /// + /// /// [Flags] internal enum STGM : uint diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGTY.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGTY.cs index 17be0418785a4..75da59f11fbb5 100644 --- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGTY.cs +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.STGTY.cs @@ -7,7 +7,7 @@ internal static partial class Ole32 { /// /// Type of the storage element. Used with . - /// + /// /// internal enum STGTY : uint { diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs index ed40ac83a8a4f..fb276985709ff 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs @@ -509,7 +509,7 @@ internal static unsafe int InitializeSecurityContext( if (inSecBuffers.Count > 1 && inUnmanagedBuffer[1].BufferType == SecurityBufferType.SECBUFFER_EXTRA && inSecBuffers._item1.Type == SecurityBufferType.SECBUFFER_EMPTY) { // OS function did not use all provided data and turned EMPTY to EXTRA - // https://docs.microsoft.com/en-us/windows/win32/secauthn/extra-buffers-returned-by-schannel + // https://learn.microsoft.com/windows/win32/secauthn/extra-buffers-returned-by-schannel int leftover = inUnmanagedBuffer[1].cbBuffer; int processed = inSecBuffers._item0.Token.Length - inUnmanagedBuffer[1].cbBuffer; @@ -810,7 +810,7 @@ internal static unsafe int AcceptSecurityContext( if (inSecBuffers.Count > 1 && inUnmanagedBuffer[1].BufferType == SecurityBufferType.SECBUFFER_EXTRA && inSecBuffers._item1.Type == SecurityBufferType.SECBUFFER_EMPTY) { // OS function did not use all provided data and turned EMPTY to EXTRA - // https://docs.microsoft.com/en-us/windows/win32/secauthn/extra-buffers-returned-by-schannel + // https://learn.microsoft.com/windows/win32/secauthn/extra-buffers-returned-by-schannel int leftover = inUnmanagedBuffer[1].cbBuffer; int processed = inSecBuffers._item0.Token.Length - inUnmanagedBuffer[1].cbBuffer; diff --git a/src/libraries/Common/src/System/Data/Common/AdapterUtil.cs b/src/libraries/Common/src/System/Data/Common/AdapterUtil.cs index 3675641903c36..956e6ca855e2e 100644 --- a/src/libraries/Common/src/System/Data/Common/AdapterUtil.cs +++ b/src/libraries/Common/src/System/Data/Common/AdapterUtil.cs @@ -17,7 +17,7 @@ namespace System.Data.Common { internal static partial class ADP { - // NOTE: Initializing a Task in SQL CLR requires the "UNSAFE" permission set (https://docs.microsoft.com/en-us/dotnet/framework/performance/sql-server-programming-and-host-protection-attributes) + // NOTE: Initializing a Task in SQL CLR requires the "UNSAFE" permission set (https://learn.microsoft.com/dotnet/framework/performance/sql-server-programming-and-host-protection-attributes) // Therefore we are lazily initializing these Tasks to avoid forcing customers to use the "UNSAFE" set when they are actually using no Async features private static Task? _trueTask; internal static Task TrueTask => _trueTask ??= Task.FromResult(true); diff --git a/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs b/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs index ce7f345b0ea9c..92f75742af350 100644 --- a/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs +++ b/src/libraries/Common/src/System/Diagnostics/DiagnosticsHelper.cs @@ -19,7 +19,7 @@ internal static class DiagnosticsHelper /// we avoid the allocation of a new array by using the second collection as is and not converting it to an array. the reason /// is we call this every time we try to create a meter or instrument and we don't want to allocate a new array every time. /// - internal static bool CompareTags(List>? sortedTags, IEnumerable>? tags2) + internal static bool CompareTags(IList>? sortedTags, IEnumerable>? tags2) { if (sortedTags == tags2) { diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 5f00955e0e298..10ef243c6318e 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -9,6 +9,7 @@ using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; +using System.Text; using System.Text.Unicode; #endif @@ -261,7 +262,7 @@ public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span vec1 = Vector128.LoadUnsafe(ref srcRef, offset); Vector128 vec2 = Vector128.LoadUnsafe(ref srcRef, offset + (nuint)Vector128.Count); - Vector128 vec = Vector128.Narrow(vec1, vec2); + Vector128 vec = Ascii.ExtractAsciiVector(vec1, vec2); // Based on "Algorithm #3" https://github.com/WojciechMula/toys/blob/master/simd-parse-hex/geoff_algorithm.cpp // by Geoff Langdale and Wojciech Mula diff --git a/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs b/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs index c30e77bfa1b8b..d4f4ef7795f10 100644 --- a/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs +++ b/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs @@ -104,7 +104,7 @@ public bool GetProxyForUrl( autoProxyOptions.Reserved2 = 0; // AutoProxy Cache. - // https://docs.microsoft.com/en-us/windows/desktop/WinHttp/autoproxy-cache + // https://learn.microsoft.com/windows/desktop/WinHttp/autoproxy-cache // If the out-of-process service is active when WinHttpGetProxyForUrl is called, the cached autoproxy // URL and script are available to the whole computer. However, if the out-of-process service is used, // and the fAutoLogonIfChallenged flag in the pAutoProxyOptions structure is true, then the autoproxy diff --git a/src/libraries/Common/src/System/Net/Security/TargetHostNameHelper.cs b/src/libraries/Common/src/System/Net/Security/TargetHostNameHelper.cs index 7202cebe142ba..4767e1e4ff490 100644 --- a/src/libraries/Common/src/System/Net/Security/TargetHostNameHelper.cs +++ b/src/libraries/Common/src/System/Net/Security/TargetHostNameHelper.cs @@ -32,7 +32,7 @@ internal static string NormalizeHostName(string? targetHost) } catch (ArgumentException) when (IsSafeDnsString(targetHost)) { - // Seems like name that does not confrom to IDN but apers somewhat valid according to original DNS rfc. + // Seems like name that does not conform to IDN but appears somewhat valid according to original DNS rfc. } return targetHost; diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs index f877d3b729896..44c1d47d96dca 100644 --- a/src/libraries/Common/src/System/Number.NumberBuffer.cs +++ b/src/libraries/Common/src/System/Number.NumberBuffer.cs @@ -29,9 +29,8 @@ internal unsafe ref struct NumberBuffer public bool IsNegative; public bool HasNonZeroTail; public NumberBufferKind Kind; - public byte* DigitsPtr; - public int DigitsLength; - public readonly Span Digits => new Span(DigitsPtr, DigitsLength); + public Span Digits; + public readonly byte* DigitsPtr => (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(Digits)); // safe since constructor expects Digits to refer to unmovable memory public NumberBuffer(NumberBufferKind kind, byte* digits, int digitsLength) : this(kind, new Span(digits, digitsLength)) { @@ -50,8 +49,7 @@ public NumberBuffer(NumberBufferKind kind, Span digits) IsNegative = false; HasNonZeroTail = false; Kind = kind; - DigitsPtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(digits)); // Safe since memory must be fixed - DigitsLength = digits.Length; + Digits = digits; #if DEBUG Digits.Fill(0xCC); #endif diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs b/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs index c076e8c9a68b4..6b3c8eabfd4cb 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSACng.SignVerify.cs @@ -14,7 +14,7 @@ public sealed partial class DSACng : DSA // As of FIPS 186-4 the maximum Q size is 32 bytes. // // See also: cbGroupSize at - // https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_dsa_key_blob_v2 + // https://learn.microsoft.com/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_dsa_key_blob_v2 private const int WindowsMaxQSize = 32; public override byte[] CreateSignature(byte[] rgbHash) diff --git a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs index 51e1cccb43e30..824e3ff409315 100644 --- a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs +++ b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs @@ -5,13 +5,14 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace System.Text.Json { - internal sealed class PooledByteBufferWriter : IBufferWriter, IDisposable + internal sealed class PooledByteBufferWriter : PipeWriter, IDisposable { // This class allows two possible configurations: if rentedBuffer is not null then // it can be used as an IBufferWriter and holds a buffer that should eventually be @@ -19,6 +20,7 @@ internal sealed class PooledByteBufferWriter : IBufferWriter, IDisposable // cleared/disposed state and it must re-rent a buffer before it can be used again. private byte[]? _rentedBuffer; private int _index; + private readonly Stream? _stream; private const int MinimumBufferSize = 256; @@ -41,6 +43,11 @@ public PooledByteBufferWriter(int initialCapacity) : this() _index = 0; } + public PooledByteBufferWriter(int initialCapacity, Stream stream) : this(initialCapacity) + { + _stream = stream; + } + public ReadOnlyMemory WrittenMemory { get @@ -127,7 +134,7 @@ public void InitializeEmptyInstance(int initialCapacity) public static PooledByteBufferWriter CreateEmptyInstanceForCaching() => new PooledByteBufferWriter(); - public void Advance(int count) + public override void Advance(int count) { Debug.Assert(_rentedBuffer != null); Debug.Assert(count >= 0); @@ -135,35 +142,24 @@ public void Advance(int count) _index += count; } - public Memory GetMemory(int sizeHint = MinimumBufferSize) + public override Memory GetMemory(int sizeHint = MinimumBufferSize) { CheckAndResizeBuffer(sizeHint); return _rentedBuffer.AsMemory(_index); } - public Span GetSpan(int sizeHint = MinimumBufferSize) + public override Span GetSpan(int sizeHint = MinimumBufferSize) { CheckAndResizeBuffer(sizeHint); return _rentedBuffer.AsSpan(_index); } #if NET - internal ValueTask WriteToStreamAsync(Stream destination, CancellationToken cancellationToken) - { - return destination.WriteAsync(WrittenMemory, cancellationToken); - } - internal void WriteToStream(Stream destination) { destination.Write(WrittenMemory.Span); } #else - internal Task WriteToStreamAsync(Stream destination, CancellationToken cancellationToken) - { - Debug.Assert(_rentedBuffer != null); - return destination.WriteAsync(_rentedBuffer, 0, _index, cancellationToken); - } - internal void WriteToStream(Stream destination) { Debug.Assert(_rentedBuffer != null); @@ -217,6 +213,28 @@ private void CheckAndResizeBuffer(int sizeHint) Debug.Assert(_rentedBuffer.Length - _index > 0); Debug.Assert(_rentedBuffer.Length - _index >= sizeHint); } + + public override async ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + Debug.Assert(_stream is not null); +#if NET + await _stream.WriteAsync(WrittenMemory, cancellationToken).ConfigureAwait(false); +#else + Debug.Assert(_rentedBuffer != null); + await _stream.WriteAsync(_rentedBuffer, 0, _index, cancellationToken).ConfigureAwait(false); +#endif + Clear(); + + return new FlushResult(isCanceled: false, isCompleted: false); + } + + public override bool CanGetUnflushedBytes => true; + public override long UnflushedBytes => _index; + + // This type is used internally in JsonSerializer to help buffer and flush bytes to the underlying Stream. + // It's only pretending to be a PipeWriter and doesn't need Complete or CancelPendingFlush for the internal usage. + public override void CancelPendingFlush() => throw new NotImplementedException(); + public override void Complete(Exception? exception = null) => throw new NotImplementedException(); } internal static partial class ThrowHelper diff --git a/src/libraries/Common/tests/System/IO/TempDirectory.cs b/src/libraries/Common/tests/System/IO/TempDirectory.cs index 280315471f6d9..e15f00a199a65 100644 --- a/src/libraries/Common/tests/System/IO/TempDirectory.cs +++ b/src/libraries/Common/tests/System/IO/TempDirectory.cs @@ -47,7 +47,7 @@ protected virtual void DeleteDirectory() /// /// Generates a string with 255 random valid filename characters. /// 255 is the max file/folder name length in NTFS and FAT32: - // https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison?redirectedfrom=MSDN#limits + // https://learn.microsoft.com/windows/win32/fileio/filesystem-functionality-comparison?redirectedfrom=MSDN#limits /// /// A 255 length string with random valid filename characters. public static string GetMaxLengthRandomName() diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs index 5635b753a5f6c..581bc018a1983 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs @@ -75,7 +75,7 @@ public override async ValueTask DisposeAsync() // Dispose the connection // If we already waited for graceful shutdown from the client, then the connection is already closed and this will simply release the handle. // If not, then this will silently abort the connection. - await _connection.DisposeAsync(); + await _connection.DisposeAsync().ConfigureAwait(false); // Dispose control streams so that we release their handles too. if (_inboundControlStream is not null) @@ -92,12 +92,12 @@ public override async ValueTask DisposeAsync() public async ValueTask OpenUnidirectionalStreamAsync() { - return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional)); + return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional).ConfigureAwait(false)); } public async ValueTask OpenBidirectionalStreamAsync() { - return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional)); + return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional).ConfigureAwait(false)); } public static int GetRequestId(QuicStream stream) @@ -141,10 +141,10 @@ async Task EnsureControlStreamAcceptedInternalAsync() _delayedStreams.Enqueue(quicStream); } - long? streamType = await controlStream.ReadIntegerAsync(); + long? streamType = await controlStream.ReadIntegerAsync().ConfigureAwait(false); Assert.Equal(Http3LoopbackStream.ControlStream, streamType); - List<(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync(); + List<(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync().ConfigureAwait(false); (long settingId, long settingValue) = Assert.Single(settings); Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId); @@ -177,7 +177,7 @@ public async Task AcceptRequestStreamAsync() public async Task<(Http3LoopbackStream clientControlStream, Http3LoopbackStream requestStream)> AcceptControlAndRequestStreamAsync() { - Http3LoopbackStream requestStream = await AcceptRequestStreamAsync(); + Http3LoopbackStream requestStream = await AcceptRequestStreamAsync().ConfigureAwait(false); Http3LoopbackStream controlStream = _inboundControlStream; return (controlStream, requestStream); @@ -185,9 +185,9 @@ public async Task AcceptRequestStreamAsync() public async Task EstablishControlStreamAsync(SettingsEntry[] settingsEntries) { - _outboundControlStream = await OpenUnidirectionalStreamAsync(); - await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream); - await _outboundControlStream.SendSettingsFrameAsync(settingsEntries); + _outboundControlStream = await OpenUnidirectionalStreamAsync().ConfigureAwait(false); + await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream).ConfigureAwait(false); + await _outboundControlStream.SendSettingsFrameAsync(settingsEntries).ConfigureAwait(false); } public async Task DisposeCurrentStream() @@ -213,7 +213,7 @@ public override async Task ReadRequestDataAsync(bool readBody = public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "", bool isFinal = true) { - await _currentStream.SendResponseAsync(statusCode, headers, content, isFinal); + await _currentStream.SendResponseAsync(statusCode, headers, content, isFinal).ConfigureAwait(false); if (isFinal) { await DisposeCurrentStream().ConfigureAwait(false); @@ -222,7 +222,7 @@ public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpSta public override async Task SendResponseBodyAsync(byte[] content, bool isFinal = true) { - await _currentStream.SendResponseBodyAsync(content, isFinal); + await _currentStream.SendResponseBodyAsync(content, isFinal).ConfigureAwait(false); if (isFinal) { await DisposeCurrentStream().ConfigureAwait(false); @@ -249,11 +249,11 @@ public override async Task HandleRequestAsync(HttpStatusCode st // So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection. // Note that in HTTP3 (unlike HTTP2) there is no strict ordering between the GOAWAY and the response below; // so the client may race in processing them and we need to handle this. - await _outboundControlStream.SendGoAwayFrameAsync(stream.StreamId + 4); + await _outboundControlStream.SendGoAwayFrameAsync(stream.StreamId + 4).ConfigureAwait(false); await stream.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false); - await WaitForClientDisconnectAsync(); + await WaitForClientDisconnectAsync().ConfigureAwait(false); return request; } @@ -263,7 +263,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false) try { long firstInvalidStreamId = failCurrentRequest ? _currentStreamId : _currentStreamId + 4; - await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId); + await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId).ConfigureAwait(false); } catch (QuicException abortException) when (abortException.QuicError == QuicError.ConnectionAborted && abortException.ApplicationErrorCode == H3_NO_ERROR) { @@ -283,7 +283,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false) return; } - await WaitForClientDisconnectAsync(); + await WaitForClientDisconnectAsync().ConfigureAwait(false); } // Wait for the client to close the connection, e.g. after we send a GOAWAY, or after the HttpClient is disposed. @@ -315,10 +315,10 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true) // The client's control stream should throw QuicConnectionAbortedException, indicating that it was // aborted because the connection was closed (and was not explicitly closed or aborted prior to the connection being closed) - QuicException ex = await Assert.ThrowsAsync(async () => await _inboundControlStream.ReadFrameAsync()); + QuicException ex = await Assert.ThrowsAsync(async () => await _inboundControlStream.ReadFrameAsync().ConfigureAwait(false)); Assert.Equal(QuicError.ConnectionAborted, ex.QuicError); - await CloseAsync(H3_NO_ERROR); + await CloseAsync(H3_NO_ERROR).ConfigureAwait(false); } public override async Task WaitForCancellationAsync(bool ignoreIncomingData = true) diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index 63368b49c129f..f6fbdf1f8cc14 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -6,7 +6,6 @@ using System.IO; using System.Net.Quic; using System.Net.Security; -using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; @@ -71,13 +70,13 @@ private async Task EstablishHttp3ConnectionAsync(params QuicConnection con = await _listener.AcceptConnectionAsync().ConfigureAwait(false); Http3LoopbackConnection connection = new Http3LoopbackConnection(con); - await connection.EstablishControlStreamAsync(settingsEntries); + await connection.EstablishControlStreamAsync(settingsEntries).ConfigureAwait(false); return connection; } public override async Task EstablishGenericConnectionAsync() { - return await EstablishHttp3ConnectionAsync(); + return await EstablishHttp3ConnectionAsync().ConfigureAwait(false); } public Task EstablishConnectionAsync(params SettingsEntry[] settingsEntries) @@ -89,12 +88,12 @@ public override async Task AcceptConnectionAsync(Func HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { - await using Http3LoopbackConnection con = (Http3LoopbackConnection)await EstablishGenericConnectionAsync().ConfigureAwait(false); + await using Http3LoopbackConnection con = await EstablishHttp3ConnectionAsync().ConfigureAwait(false); return await con.HandleRequestAsync(statusCode, headers, content).ConfigureAwait(false); } } @@ -113,7 +112,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option public override async Task CreateServerAsync(Func funcAsync, int millisecondsTimeout = LoopbackServerTimeoutMilliseconds, GenericLoopbackOptions options = null) { using GenericLoopbackServer server = CreateServer(options); - await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); + await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)).ConfigureAwait(false); } public override Task CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null) diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs index d8d0f9a7a7caf..778919eb4863d 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs @@ -124,7 +124,7 @@ private async Task SendPartialHeadersFrameAsync(HttpStatusCode? statusCode, IEnu { Memory payload = ConstructHeadersPayload(statusCode, headers); - await SendFrameHeaderAsync(HeadersFrame, payload.Length); + await SendFrameHeaderAsync(HeadersFrame, payload.Length).ConfigureAwait(false); // Slice off final byte so the payload is not complete payload = payload.Slice(0, payload.Length - 1); @@ -144,7 +144,7 @@ public async Task SendGoAwayFrameAsync(long firstInvalidStreamId) int bytesWritten = 0; bytesWritten += EncodeHttpInteger(firstInvalidStreamId, buffer); - await SendFrameAsync(GoAwayFrame, buffer.AsMemory(0, bytesWritten)); + await SendFrameAsync(GoAwayFrame, buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false); } private async Task SendFrameHeaderAsync(long frameType, int payloadLength) @@ -367,11 +367,11 @@ async Task WaitForReadCancellation() { if (ignoreIncomingData) { - await DrainResponseData(); + await DrainResponseData().ConfigureAwait(false); } else { - int bytesRead = await _stream.ReadAsync(new byte[1]); + int bytesRead = await _stream.ReadAsync(new byte[1]).ConfigureAwait(false); if (bytesRead != 0) { throw new Exception($"Unexpected data received while waiting for client cancllation."); @@ -388,7 +388,7 @@ async Task WaitForWriteCancellation() { try { - await _stream.WritesClosed; + await _stream.WritesClosed.ConfigureAwait(false); } catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted && ex.ApplicationErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED) { @@ -396,7 +396,7 @@ async Task WaitForWriteCancellation() } } - await Task.WhenAll(WaitForReadCancellation(), WaitForWriteCancellation()); + await Task.WhenAll(WaitForReadCancellation(), WaitForWriteCancellation()).ConfigureAwait(false); if (!readCanceled && !writeCanceled) { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index 6ecd7261ca2d9..7cd7c4d6f1697 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -44,7 +44,7 @@ private static CookieContainer CreateSingleCookieContainer(Uri uri, string cooki private static string GetCookieHeaderValue(string cookieName, string cookieValue) => $"{cookieName}={cookieValue}"; [Fact] - public virtual async Task GetAsync_DefaultCoookieContainer_NoCookieSent() + public async Task GetAsync_DefaultCoookieContainer_NoCookieSent() { await LoopbackServerFactory.CreateClientAndServerAsync( async uri => @@ -216,15 +216,10 @@ private string GetCookieValue(HttpRequestData request) return cookieHeaderValue; } - [ConditionalFact] + [Fact] [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndCookieHeader_BothCookiesSent() { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/101377"); - } - await LoopbackServerFactory.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs index 4ad8e2005a928..0924f2357ac4d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs @@ -90,12 +90,12 @@ public async Task ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed(boo const string ExpectedPassword = "rightpassword"; LoopbackServer.Options options = new LoopbackServer.Options { IsProxy = true, Username = ExpectedUsername, Password = ExpectedPassword }; - await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() => + await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(async () => { var psi = new ProcessStartInfo(); psi.Environment.Add("http_proxy", $"http://{uri.Host}:{uri.Port}"); - RemoteExecutor.Invoke(async (useProxyString, useVersionString, uriString) => + await RemoteExecutor.Invoke(async (useProxyString, useVersionString, uriString) => { using (HttpClientHandler handler = CreateHttpClientHandler(useVersionString)) using (HttpClient client = CreateHttpClient(handler, useVersionString)) @@ -111,7 +111,7 @@ await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() => }, useProxy.ToString(), UseVersion.ToString(), // If proxy is used , the url does not matter. We set it to be different to avoid confusion. useProxy ? Configuration.Http.RemoteEchoServer.ToString() : uri.ToString(), - new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync(); }), server => server.AcceptConnectionAsync(async connection => { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index 236af85b26035..312eb90210815 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -382,7 +382,7 @@ public async Task PostAsync_Post_ChannelBinding_ConfiguredCorrectly() [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [PlatformSpecific(TestPlatforms.Linux)] - public void HttpClientUsesSslCertEnvironmentVariables() + public async Task HttpClientUsesSslCertEnvironmentVariables() { // We set SSL_CERT_DIR and SSL_CERT_FILE to empty locations. // The HttpClient should fail to validate the server certificate. @@ -395,7 +395,7 @@ public void HttpClientUsesSslCertEnvironmentVariables() File.WriteAllText(sslCertFile, ""); psi.Environment.Add("SSL_CERT_FILE", sslCertFile); - RemoteExecutor.Invoke(async (useVersionString, allowAllCertificatesString) => + await RemoteExecutor.Invoke(async (useVersionString, allowAllCertificatesString) => { const string Url = "https://www.microsoft.com"; var version = Version.Parse(useVersionString); @@ -405,7 +405,7 @@ public void HttpClientUsesSslCertEnvironmentVariables() using HttpClient client = CreateHttpClient(handler, useVersionString); await Assert.ThrowsAsync(() => client.GetAsync(Url)); - }, UseVersion.ToString(), AllowAllCertificates.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + }, UseVersion.ToString(), AllowAllCertificates.ToString(), new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync(); } } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 31d8d9f154fc4..bfdd5a0e5aff5 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Net.Http.Headers; -using System.Net.Sockets; #if !NETFRAMEWORK using System.Net.Quic; #endif @@ -989,7 +988,7 @@ await connection.WriteStringAsync( }); } - [ConditionalTheory] + [Theory] [InlineData(true, true, true)] [InlineData(true, true, false)] [InlineData(true, false, false)] @@ -999,11 +998,6 @@ await connection.WriteStringAsync( [ActiveIssue("https://github.com/dotnet/runtime/issues/65429", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJS))] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked, bool enableWasmStreaming, bool slowChunks) { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); - } - if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) { return; @@ -2027,22 +2021,6 @@ public async Task SendAsync_RequestVersion11_ServerReceivesVersion11Request() Assert.Equal(new Version(1, 1), receivedRequestVersion); } - [SkipOnPlatform(TestPlatforms.Browser, "Version is not supported on Browser")] - [Fact] - public async Task SendAsync_RequestVersionNotSpecified_ServerReceivesVersion11Request() - { - // SocketsHttpHandler treats 0.0 as a bad version, and throws. - if (!IsWinHttpHandler) - { - return; - } - - // The default value for HttpRequestMessage.Version is Version(1,1). - // So, we need to set something different (0,0), to test the "unknown" version. - Version receivedRequestVersion = await SendRequestAndGetRequestVersionAsync(new Version(0, 0)); - Assert.Equal(new Version(1, 1), receivedRequestVersion); - } - private async Task SendRequestAndGetRequestVersionAsync(Version requestVersion) { Version receivedRequestVersion = null; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs index d5c61f82246de..c75c6de51f6c9 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs @@ -88,14 +88,9 @@ public async Task GetAsync_RequestVersion0X_ThrowsNotSupportedException(int mino } [Theory] - [InlineData(1, 2)] - [InlineData(1, 6)] - [InlineData(2, 0)] // Note, this is plain HTTP (not HTTPS), so 2.0 is not supported and should degrade to 1.1 - [InlineData(2, 1)] - [InlineData(2, 7)] + [InlineData(2, 0)] // This is plain HTTP (not HTTPS), so 2.0 is not supported and should degrade to 1.1 [InlineData(3, 0)] - [InlineData(4, 2)] - public async Task GetAsync_UnknownRequestVersion_DegradesTo11(int majorVersion, int minorVersion) + public async Task GetAsync_PlainHttpRequestOnVersion20Or30_DegradesTo11(int majorVersion, int minorVersion) { // Sync API supported only up to HTTP/1.1 if (!TestAsync && majorVersion >= 2) @@ -120,6 +115,35 @@ await LoopbackServer.CreateServerAsync(async (server, url) => }, new LoopbackServer.Options { StreamWrapper = GetStream }); } + [Theory] + [InlineData(0, 0)] + [InlineData(1, 2)] + [InlineData(1, 6)] + [InlineData(2, 1)] + [InlineData(2, 7)] + [InlineData(4, 2)] + public async Task GetAsync_UnknownRequestVersion_ThrowsException(int majorVersion, int minorVersion) + { + // Sync API supported only up to HTTP/1.1 + if (!TestAsync && majorVersion >= 2) + { + return; + } + + await LoopbackServer.CreateServerAsync(async (server, url) => + { + Version version = new Version(majorVersion, minorVersion); + using (HttpClient client = CreateHttpClient(version.ToString())) + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); + request.Version = version; + Task getResponseTask = client.SendAsync(TestAsync, request); + Task> serverTask = server.AcceptConnectionSendResponseAndCloseAsync(); + await Assert.ThrowsAsync(() => getResponseTask); + } + }, new LoopbackServer.Options { StreamWrapper = GetStream }); + } + [Theory] [InlineData(0)] [InlineData(1)] diff --git a/src/libraries/Common/tests/System/Net/RemoteExecutorExtensions.cs b/src/libraries/Common/tests/System/Net/RemoteExecutorExtensions.cs new file mode 100644 index 0000000000000..48ecfed7939ac --- /dev/null +++ b/src/libraries/Common/tests/System/Net/RemoteExecutorExtensions.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.DotNet.RemoteExecutor; + +namespace Microsoft.DotNet.RemoteExecutor; + +internal static class RemoteExecutorExtensions +{ + public static async ValueTask DisposeAsync(this RemoteInvokeHandle handle) + { + await Task.Run(handle.Dispose); + } +} diff --git a/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs b/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs index 1117b3412f35a..6f576d927b219 100644 --- a/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs +++ b/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs @@ -13,7 +13,7 @@ namespace System.Net.Security { // Implementation of subset of the NTLM specification - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 + // https://learn.microsoft.com/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 // // Server-side implementation of the NTLMv2 exchange is implemented with // basic verification of the messages passed by the client against a diff --git a/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs b/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs index 2817d305e6d0a..386829b2bf49e 100644 --- a/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs +++ b/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs @@ -74,7 +74,7 @@ private void CreateUser() // Create user and remove/create if already exists uint result = NetUserAdd(null, 1, ref userInfo, out uint param_err); - // error codes https://docs.microsoft.com/en-us/windows/desktop/netmgmt/network-management-error-codes + // error codes https://learn.microsoft.com/windows/desktop/netmgmt/network-management-error-codes // 0 == NERR_Success if (result == 2224) // NERR_UserExists { diff --git a/src/libraries/Fuzzing/README.md b/src/libraries/Fuzzing/README.md index 66f945708309e..2dbeeba7a612f 100644 --- a/src/libraries/Fuzzing/README.md +++ b/src/libraries/Fuzzing/README.md @@ -31,17 +31,6 @@ dotnet tool install --global SharpFuzz.CommandLine > The project uses a checked runtime + debug libraries configuration by default. > If you want to use a different configuration, make sure to also adjust the artifact paths in `nuget.config`. -### Running against a sample input - -The program accepts two arguments: the name of the fuzzer and the path to a sample input file / directory. -To run the HttpHeaders target against the `inputs` directory, use the following command: - -```cmd -cd src/libraries/Fuzzing/DotnetFuzzing - -dotnet run HttpHeadersFuzzer inputs -``` - ### Fuzzing locally The `prepare-onefuzz` command will create separate directories for each fuzzing target, instrument the relevant assemblies, and generate a helper script for running them locally. @@ -50,7 +39,7 @@ Note that this command must be ran on the published artifacts (won't work with ` ```cmd cd src/libraries/Fuzzing/DotnetFuzzing -dotnet publish -o publish && publish/DotnetFuzzing.exe prepare-onefuzz deployment +dotnet publish -o publish; publish/DotnetFuzzing.exe prepare-onefuzz deployment ``` You can now start fuzzing by running the `local-run.bat` script in the folder of the fuzzer you are interested in. @@ -92,3 +81,16 @@ internal sealed class IPAddressFuzzer : IFuzzer Once you've created the new target, you can follow instructions above to run it locally. Targets are discovered via reflection, so they will automatically become available for local runs and continuous fuzzing in CI. + +### Running against a sample input + +The program accepts two arguments: the name of the fuzzer and the path to a sample input file / directory. +To run the HttpHeaders target against the `inputs` directory, use the following command: + +```cmd +cd src/libraries/Fuzzing/DotnetFuzzing + +dotnet run HttpHeadersFuzzer inputs +``` + +This can be useful when debugging a crash, or running the fuzzer over existing inputs to collect code coverage. diff --git a/src/libraries/Microsoft.Extensions.Caching.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Caching.Abstractions/README.md index a89ce2c8c5483..83bd01f557e4f 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.Caching.Abstractions/README.md @@ -2,7 +2,7 @@ Caching is combined with a core caching abstraction under `Microsoft.Extensions.Caching.Abstractions` that allows for implementing general purpose memory and distributed caches, with integration for Redis and SqlServer. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/caching. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/caching. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/README.md b/src/libraries/Microsoft.Extensions.Caching.Memory/README.md index e2a88e23c7173..90817800a9d7b 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/README.md +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/README.md @@ -2,7 +2,7 @@ In-memory caching provides a general purpose memory implementation of the core caching abstractions provided under `Microsoft.Extensions.Caching.Abstractions` and it is great for apps that run in a single server, where all cached data rents memory in the app's process. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/caching. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/caching. ## Example diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index 179b7a65b78d2..84446ae68a5ac 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -121,7 +121,7 @@ private void EmitGetCoreMethod() Expression.sectionPath, writeOnSuccess: parsedValueExpr => _writer.WriteLine($"return {parsedValueExpr};"), checkForNullSectionValue: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue, - useDefaultValueIfSectionValueIsNull: false, + defaultValueSource: null, useIncrementalStringValueIdentifier: false); } break; @@ -204,7 +204,7 @@ private void EmitGetValueCoreMethod() Expression.sectionPath, writeOnSuccess: (parsedValueExpr) => _writer.WriteLine($"return {parsedValueExpr};"), checkForNullSectionValue: false, - useDefaultValueIfSectionValueIsNull: false, + defaultValueSource: null, useIncrementalStringValueIdentifier: false); EmitEndBlock(); @@ -325,7 +325,8 @@ private void EmitInitializeMethod(ObjectSpec type) type is { Properties: not null, ConstructorParameters: not null }, $"Expecting type for init method, {type.DisplayString}, to have both properties and ctor params."); - IEnumerable initOnlyProps = type.Properties.Where(prop => prop is { SetOnInit: true }); + IEnumerable initOnlyProps = type.Properties + .Where(prop => prop.SetOnInit && _typeIndex.ShouldBindTo(prop)); List ctorArgList = new(); EmitStartBlock($"public static {type.TypeRef.FullyQualifiedName} {GetInitializeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); @@ -349,7 +350,7 @@ private void EmitInitializeMethod(ObjectSpec type) foreach (PropertySpec property in initOnlyProps) { - if (_typeIndex.ShouldBindTo(property) && property.MatchingCtorParam is null) + if (property.MatchingCtorParam is null) { EmitBindImplForMember(property); } @@ -409,7 +410,9 @@ void EmitBindImplForMember(MemberSpec member) member, member.Name, sectionPathExpr: GetSectionPathFromConfigurationExpression(configKeyName), + // Since we're binding to local variables, we can always get and set canSet: true, + canGet: true, InitializationKind.None); if (canBindToMember) @@ -698,7 +701,7 @@ private void EmitBindingLogicForEnumerableWithAdd(TypeRef elementTypeRef, string Expression.sectionPath, (parsedValueExpr) => _writer.WriteLine($"{addExpr}({parsedValueExpr});"), checkForNullSectionValue: true, - useDefaultValueIfSectionValueIsNull: false, + defaultValueSource: null, useIncrementalStringValueIdentifier: false); } break; @@ -752,7 +755,7 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type) Expression.sectionPath, Emit_BindAndAddLogic_ForElement, checkForNullSectionValue: false, - useDefaultValueIfSectionValueIsNull: false, + defaultValueSource: null, useIncrementalStringValueIdentifier: false); void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) @@ -767,7 +770,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) Expression.sectionPath, writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {parsedValueExpr};"), checkForNullSectionValue: true, - useDefaultValueIfSectionValueIsNull: false, + defaultValueSource: null, useIncrementalStringValueIdentifier: false); } break; @@ -850,6 +853,7 @@ private void EmitBindCoreImplForObject(ObjectSpec type) memberAccessExpr: $"{containingTypeRef}.{property.Name}", GetSectionPathFromConfigurationExpression(property.ConfigurationKeyName), canSet: property.CanSet, + canGet: property.CanGet, InitializationKind.Declaration); } } @@ -860,6 +864,7 @@ private bool EmitBindImplForMember( string memberAccessExpr, string sectionPathExpr, bool canSet, + bool canGet, InitializationKind initializationKind) { string sectionParseExpr = GetSectionFromConfigurationExpression(member.ConfigurationKeyName); @@ -868,14 +873,9 @@ private bool EmitBindImplForMember( { case ParsableFromStringSpec stringParsableType: { - if (canSet) + // Reflection binder does not support binding to set-only properties + if (canSet && canGet) { - bool useDefaultValueIfSectionValueIsNull = - initializationKind == InitializationKind.Declaration && - member is PropertySpec && - member.TypeRef.IsValueType && - _typeIndex.GetTypeSpec(member.TypeRef) is not NullableSpec; - EmitBlankLineIfRequired(); EmitBindingLogic( stringParsableType, @@ -883,7 +883,7 @@ member is PropertySpec && sectionPathExpr, writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{memberAccessExpr} = {parsedValueExpr};"), checkForNullSectionValue: true, - useDefaultValueIfSectionValueIsNull, + defaultValueSource: initializationKind == InitializationKind.Declaration ? memberAccessExpr : null, useIncrementalStringValueIdentifier: true); } @@ -1073,7 +1073,7 @@ private void EmitBindingLogic( string sectionPathExpr, Action? writeOnSuccess, bool checkForNullSectionValue, - bool useDefaultValueIfSectionValueIsNull, + string? defaultValueSource, bool useIncrementalStringValueIdentifier) { StringParsableTypeKind typeKind = type.StringParsableTypeKind; @@ -1090,24 +1090,33 @@ private void EmitBindingLogic( if (!checkForNullSectionValue) { - InvokeWriteOnSuccess(); + writeOnSuccess?.Invoke(parsedValueExpr); } else { EmitStartBlock($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})"); - InvokeWriteOnSuccess(); + writeOnSuccess?.Invoke(parsedValueExpr); EmitEndBlock(); } - if (useDefaultValueIfSectionValueIsNull) + if (defaultValueSource is not null) { - parsedValueExpr = $"default"; + Debug.Assert(checkForNullSectionValue); + EmitStartBlock($"else if (defaultValueIfNotFound)"); - InvokeWriteOnSuccess(); + if (!type.TypeRef.CanBeNull) + { + writeOnSuccess?.Invoke(defaultValueSource); + } + else + { + _writer.WriteLine($"var currentValue = {defaultValueSource};"); + EmitStartBlock($"if (currentValue is not null)"); + writeOnSuccess?.Invoke("currentValue"); + EmitEndBlock(); + } EmitEndBlock(); } - - void InvokeWriteOnSuccess() => writeOnSuccess?.Invoke(parsedValueExpr); } private bool EmitObjectInit(ComplexTypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index 060d1f55d72a0..c0434440df015 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -543,7 +543,7 @@ private static bool CanBindToTheseConstructorParameters(ParameterInfo[] construc { if (p.IsOut || p.IsIn || p.ParameterType.IsByRef) { - nameOfInvalidParameter = p.Name!; // never null as we're not passed return value parameters: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.parameterinfo.name?view=net-6.0#remarks + nameOfInvalidParameter = p.Name!; // never null as we're not passed return value parameters: https://learn.microsoft.com/dotnet/api/system.reflection.parameterinfo.name?view=net-6.0#remarks return false; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs index b7ef3cba018ed..17ec28e06f358 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs @@ -161,7 +161,7 @@ public class ClassWithPrimaryCtorDefaultValues(string color = "blue", int length public string Color { get; } = color; public int Length { get; } = length; public decimal Height { get; } = height; - public EditorBrowsableState EB { get;} = eb; + public EditorBrowsableState EB { get; } = eb; } public record RecordTypeOptions(string Color, int Length); @@ -174,11 +174,13 @@ public class ClassWithMatchingParametersAndProperties public ClassWithMatchingParametersAndProperties(string Color, int Length) { _color = Color; + this.ColorFromCtor = Color; this.Length = Length; } public int Length { get; set; } + public string ColorFromCtor { get; } public string Color { get => _color; @@ -341,6 +343,10 @@ public SemiImmutableClassWithInit(string color, int length) public string Color { get; } public int Length { get; } public decimal Thickness { get; init; } + public bool WasInitOnlyCalled { get; private set; } + public decimal InitOnly { init => WasInitOnlyCalled = true; } + public bool WasPrivateGetInitOnlyCalled { get; private set; } + public decimal PrivateGetInitOnly { private get => 3.14m; init => WasPrivateGetInitOnlyCalled = true; } } public struct ValueTypeOptions @@ -476,9 +482,18 @@ public class OptionWithCollectionProperties private int _otherCode; private int _otherCodeNullable; private string _otherCodeString = "default"; + private bool _wasOtherCodeStringSet; private object _otherCodeNull; private Uri _otherCodeUri; private ICollection blacklist = new HashSet(); + private string? _StringWithNullDefault; + private bool _wasStringWithNullDefaultSet; + private int _IntWithDefault = 123; + private bool _WasIntWithDefaultSet; + private SimplePocoWithOnlyDefaults _PocoWithDefault = new SimplePocoWithOnlyDefaults(); + private bool _WasPocoWithDefaultSet; + private List _PocoListWithDefault = new List { new SimplePocoWithOnlyDefaults() }; + private bool _WasPocoListWithDefaultSet; public ICollection Blacklist { @@ -512,9 +527,15 @@ public int? OtherCodeNullable public string OtherCodeString { get => _otherCodeString; - set => _otherCodeString = value; + set + { + _otherCodeString = value; + _wasOtherCodeStringSet = true; + } } + public bool WasOtherCodeStringSet => _wasOtherCodeStringSet; + public object? OtherCodeNull { get => _otherCodeNull; @@ -526,6 +547,68 @@ public Uri OtherCodeUri get => _otherCodeUri; set => _otherCodeUri = value is null ? new Uri("hello") : value; } + + public string? StringWithNullDefault + { + get => _StringWithNullDefault; + set + { + _StringWithNullDefault = value; + _wasStringWithNullDefaultSet = true; + } + } + + public bool WasStringWithNullDefaultSet => _wasStringWithNullDefaultSet; + + public int IntWithDefault + { + get => _IntWithDefault; + set + { + _IntWithDefault = value; + _WasIntWithDefaultSet = true; + } + } + + public bool WasIntWithDefaultSet => _WasIntWithDefaultSet; + + public SimplePocoWithOnlyDefaults PocoWithDefault + { + get => _PocoWithDefault; + set + { + _PocoWithDefault = value; + _WasPocoWithDefaultSet = true; + } + } + + public bool WasPocoWithDefaultSet => _WasPocoWithDefaultSet; + + public List PocoListWithDefault + { + get => _PocoListWithDefault; + set + { + _PocoListWithDefault = value; + _WasPocoListWithDefaultSet = true; + } + } + + public bool WasPocoListWithDefaultSet => _WasPocoListWithDefaultSet; + } + + public class SimplePocoWithOnlyDefaults + { + public string Example { get; set; } = "default"; + } + + public class SetOnlyPoco + { + private bool _AnyCalled; + public bool AnyCalled => _AnyCalled; + public string SetOnly { set => _AnyCalled |= true; } + public string PrivateGetter { private get => "foo"; set => _AnyCalled |= true; } + public string InitOnly { init => _AnyCalled |= true; } } public interface ISomeInterface @@ -873,7 +956,7 @@ internal sealed class DerivedWithAnotherProp : AbstractBase { public int Value2 { get; set; } } - + internal class ClassWithAbstractProp { public AbstractBase AbstractProp { get; set; } @@ -910,16 +993,9 @@ public class SharedChildInstance_Class public class ClassThatThrowsOnSetters { - private int _myIntProperty; - - public ClassThatThrowsOnSetters() - { - _myIntProperty = 42; - } - - public int MyIntProperty + public int? MyIntProperty { - get => _myIntProperty; + get => null; set => throw new InvalidOperationException("Not expected"); } } @@ -934,7 +1010,7 @@ public class BaseForHiddenMembers { public string A { get; set; } public string B { get; set; } - public TestSettingsEnum E {get; set;} + public TestSettingsEnum E { get; set; } public virtual string C { get => CBase; set => CBase = value; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 4986778a87cee..8ad298190073a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -1375,6 +1375,25 @@ public void CanBindSemiImmutableClass_WithInitProperties() Assert.Equal(42, options.Length); Assert.Equal("Green", options.Color); Assert.Equal(1.23m, options.Thickness); + Assert.False(options.WasInitOnlyCalled); + Assert.False(options.WasPrivateGetInitOnlyCalled); + } + + [Fact] + public void DoesNotCallSetOnly() + { + var dic = new Dictionary + { + {"SetOnly", "42"}, + {"PrivateGetter", "42"}, + {"InitOnly", "42"}, + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(dic); + var config = configurationBuilder.Build(); + + var options = config.Get(); + Assert.False(options.AnyCalled); } [Fact] @@ -1500,6 +1519,7 @@ public void CanBindOnParametersAndProperties_PropertiesAreSetAfterTheConstructor var options = config.Get(); Assert.Equal(42, options.Length); + Assert.Equal("Green", options.ColorFromCtor); Assert.Equal("the color is Green", options.Color); } @@ -1786,18 +1806,33 @@ public void EnsureCallingThePropertySetter() Assert.Equal(401, options.HttpStatusCode); // exists in configuration and properly sets the property - // This doesn't exist in configuration but the setter should be called which defaults the to '2' from input of '0'. + // This doesn't exist in configuration but the setter should be called which defaults the to '2' Assert.Equal(2, options.OtherCode); + // Items not in config with non-null getter values should have setter called with getter value + Assert.Equal(123, options.IntWithDefault); + Assert.True(options.WasIntWithDefaultSet); + Assert.Equal("default", options.OtherCodeString); + Assert.True(options.WasOtherCodeStringSet); + Assert.Equal("default", options.PocoWithDefault.Example); + Assert.Equal(1, options.PocoListWithDefault.Count); + +#if !BUILDING_SOURCE_GENERATOR_TESTS + // Source generator omits calls to setters for nested objects and collections + Assert.True(options.WasPocoWithDefaultSet); + Assert.True(options.WasPocoListWithDefaultSet); +#endif + // These don't exist in configuration and setters are not called since they are nullable. Assert.Equal(0, options.OtherCodeNullable); - Assert.Equal("default", options.OtherCodeString); Assert.Null(options.OtherCodeNull); Assert.Null(options.OtherCodeUri); + Assert.Null(options.StringWithNullDefault); + Assert.False(options.WasStringWithNullDefaultSet); } [Fact] - public void EnsureNotCallingSettersWhenGivenExistingInstanceNotInConfig() + public void EnsureNotCallingSettersWhenGivenExistingNullOnInstanceNotInConfig() { var builder = new ConfigurationBuilder(); builder.AddInMemoryCollection(new KeyValuePair[] { }); @@ -1807,7 +1842,7 @@ public void EnsureNotCallingSettersWhenGivenExistingInstanceNotInConfig() // The setter for MyIntProperty throws, so this verifies that the setter is not called. config.GetSection("Dmy").Bind(instance); - Assert.Equal(42, instance.MyIntProperty); + Assert.Null(instance.MyIntProperty); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind.generated.txt index 1b64f116d2508..600f031efe4a1 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind.generated.txt @@ -134,6 +134,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -141,7 +149,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance.generated.txt index c80e48e19e158..ed5a0bb604122 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance.generated.txt @@ -98,6 +98,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -105,7 +113,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt index efb7616cd1b90..78fe9de943c24 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt @@ -98,6 +98,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -105,7 +113,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Key_Instance.generated.txt index c8f211fe59de7..0ee5e6704c859 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Key_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Bind_Key_Instance.generated.txt @@ -98,6 +98,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -105,7 +113,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get.generated.txt index c9b1f23de26c7..f6c9df459af26 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get.generated.txt @@ -132,6 +132,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -139,7 +147,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) @@ -177,7 +185,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T.generated.txt index 470d6857874f5..fd3c929700477 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T.generated.txt @@ -113,6 +113,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value3) { @@ -120,7 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T_BinderOptions.generated.txt index 3f0d165c40c14..201d628018b3c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_T_BinderOptions.generated.txt @@ -113,6 +113,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value3) { @@ -120,7 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf.generated.txt index 5b5050214620b..b5a93a34f7fe3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf.generated.txt @@ -76,7 +76,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt index 039e2eecef843..b908df33810cd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt @@ -76,7 +76,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/BindConfiguration.generated.txt index 87515b8b4b326..9d8efcab38e62 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/BindConfiguration.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/BindConfiguration.generated.txt @@ -116,6 +116,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -123,7 +131,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T.generated.txt index 8b31876d5bac8..6ea010083977c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T.generated.txt @@ -122,6 +122,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -129,7 +137,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T_BinderOptions.generated.txt index e58ef0662ee21..d05f521ceb110 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Bind_T_BinderOptions.generated.txt @@ -116,6 +116,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -123,7 +131,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Primitives.generated.txt index 39cfc67e44f95..a921ccfad060c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Primitives.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Primitives.generated.txt @@ -66,7 +66,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop0 = default; + instance.Prop0 = instance.Prop0; } if (configuration["Prop1"] is string value1) @@ -75,7 +75,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop1 = default; + instance.Prop1 = instance.Prop1; } if (configuration["Prop2"] is string value2) @@ -84,7 +84,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop2 = default; + instance.Prop2 = instance.Prop2; } if (configuration["Prop3"] is string value3) @@ -93,7 +93,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop3 = default; + instance.Prop3 = instance.Prop3; } if (configuration["Prop4"] is string value4) @@ -102,13 +102,21 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop4 = default; + instance.Prop4 = instance.Prop4; } if (configuration["Prop5"] is string value5) { instance.Prop5 = value5; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop5; + if (currentValue is not null) + { + instance.Prop5 = currentValue; + } + } if (configuration["Prop6"] is string value6) { @@ -116,7 +124,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop6 = default; + instance.Prop6 = instance.Prop6; } if (configuration["Prop8"] is string value7) @@ -125,7 +133,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop8 = default; + instance.Prop8 = instance.Prop8; } if (configuration["Prop9"] is string value8) @@ -134,7 +142,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop9 = default; + instance.Prop9 = instance.Prop9; } if (configuration["Prop10"] is string value9) @@ -143,7 +151,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop10 = default; + instance.Prop10 = instance.Prop10; } if (configuration["Prop13"] is string value10) @@ -152,7 +160,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop13 = default; + instance.Prop13 = instance.Prop13; } if (configuration["Prop14"] is string value11) @@ -161,7 +169,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop14 = default; + instance.Prop14 = instance.Prop14; } if (configuration["Prop15"] is string value12) @@ -170,18 +178,34 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop15 = default; + instance.Prop15 = instance.Prop15; } if (configuration["Prop16"] is string value13) { instance.Prop16 = value13; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop16; + if (currentValue is not null) + { + instance.Prop16 = currentValue; + } + } if (configuration["Prop17"] is string value14) { instance.Prop17 = ParseSystemGlobalizationCultureInfo(value14, configuration.GetSection("Prop17").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop17; + if (currentValue is not null) + { + instance.Prop17 = currentValue; + } + } if (configuration["Prop19"] is string value15) { @@ -189,7 +213,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop19 = default; + instance.Prop19 = instance.Prop19; } if (configuration["Prop20"] is string value16) @@ -198,7 +222,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop20 = default; + instance.Prop20 = instance.Prop20; } if (configuration["Prop21"] is string value17) @@ -207,7 +231,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop21 = default; + instance.Prop21 = instance.Prop21; } if (configuration["Prop23"] is string value18) @@ -216,7 +240,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop23 = default; + instance.Prop23 = instance.Prop23; } if (configuration["Prop24"] is string value19) @@ -225,18 +249,34 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop24 = default; + instance.Prop24 = instance.Prop24; } if (configuration["Prop25"] is string value20) { instance.Prop25 = ParseSystemUri(value20, configuration.GetSection("Prop25").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop25; + if (currentValue is not null) + { + instance.Prop25 = currentValue; + } + } if (configuration["Prop26"] is string value21) { instance.Prop26 = ParseSystemVersion(value21, configuration.GetSection("Prop26").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop26; + if (currentValue is not null) + { + instance.Prop26 = currentValue; + } + } if (configuration["Prop27"] is string value22) { @@ -244,13 +284,21 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop27 = default; + instance.Prop27 = instance.Prop27; } if (configuration["Prop28"] is string value23) { instance.Prop28 = ParseByteArray(value23, configuration.GetSection("Prop28").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop28; + if (currentValue is not null) + { + instance.Prop28 = currentValue; + } + } if (configuration["Prop29"] is string value24) { @@ -258,7 +306,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop29 = default; + instance.Prop29 = instance.Prop29; } if (configuration["Prop30"] is string value25) @@ -267,7 +315,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop30 = default; + instance.Prop30 = instance.Prop30; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt index 9da38162f69c6..3cdb19e7a96fc 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt @@ -112,7 +112,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -149,6 +149,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -156,7 +164,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt index c61cb79650c6c..7796b06b64f74 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -112,7 +112,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -149,6 +149,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -156,7 +164,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt index 72e2503fd5b27..2e317ed0c14ad 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt @@ -112,7 +112,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -149,6 +149,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -156,7 +164,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index e09478cf4aed2..099fc15a892f1 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -106,7 +106,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -143,6 +143,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -150,7 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/UnsupportedTypes.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/UnsupportedTypes.generated.txt index d9d9ddc9e7fd8..ae61aaeaf2372 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/UnsupportedTypes.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/UnsupportedTypes.generated.txt @@ -134,6 +134,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.Name = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Name; + if (currentValue is not null) + { + instance.Name = currentValue; + } + } if (configuration["Age"] is string value3) { @@ -141,7 +149,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Age = default; + instance.Age = instance.Age; } if (AsConfigWithChildren(configuration.GetSection("List")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind.generated.txt index 15bfede964efd..ff6d04d6d77ab 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind.generated.txt @@ -125,6 +125,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -132,7 +140,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance.generated.txt index e3baa0a479610..b9230063940ac 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance.generated.txt @@ -95,6 +95,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -102,7 +110,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt index 975b2bf790001..ce45b4026beae 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt @@ -95,6 +95,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -102,7 +110,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Key_Instance.generated.txt index f8a85a2c6ea1a..592485538090e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Key_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Bind_Key_Instance.generated.txt @@ -95,6 +95,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value0; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value1) { @@ -102,7 +110,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get.generated.txt index 210cc441463f9..d8a63e2453ca2 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get.generated.txt @@ -129,6 +129,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -136,7 +144,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) @@ -174,7 +182,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T.generated.txt index 7e8a129fb206f..77291a823b0e5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T.generated.txt @@ -110,6 +110,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value3) { @@ -117,7 +125,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T_BinderOptions.generated.txt index 7e265b8924196..95445234ebcce 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_T_BinderOptions.generated.txt @@ -110,6 +110,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value3) { @@ -117,7 +125,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf.generated.txt index fbfde08c46327..90132c073af62 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf.generated.txt @@ -73,7 +73,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt index 8e4a799bcdd0b..6cce5a85a2d64 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt @@ -73,7 +73,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/BindConfiguration.generated.txt index 0ac91850d67a6..776a7f171230a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/BindConfiguration.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/BindConfiguration.generated.txt @@ -107,6 +107,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -114,7 +122,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T.generated.txt index bd896297fec6b..cd2be55722716 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T.generated.txt @@ -113,6 +113,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -120,7 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T_BinderOptions.generated.txt index 56682ad884a6a..665dcf42138b3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/OptionsBuilder/Bind_T_BinderOptions.generated.txt @@ -107,6 +107,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value1; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value2) { @@ -114,7 +122,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/Primitives.generated.txt index 31cd91c8cfd0b..25d8e1f053bd2 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/Primitives.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/Primitives.generated.txt @@ -63,7 +63,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop0 = default; + instance.Prop0 = instance.Prop0; } if (configuration["Prop1"] is string value1) @@ -72,7 +72,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop1 = default; + instance.Prop1 = instance.Prop1; } if (configuration["Prop2"] is string value2) @@ -81,7 +81,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop2 = default; + instance.Prop2 = instance.Prop2; } if (configuration["Prop3"] is string value3) @@ -90,7 +90,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop3 = default; + instance.Prop3 = instance.Prop3; } if (configuration["Prop4"] is string value4) @@ -99,13 +99,21 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop4 = default; + instance.Prop4 = instance.Prop4; } if (configuration["Prop5"] is string value5) { instance.Prop5 = value5; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop5; + if (currentValue is not null) + { + instance.Prop5 = currentValue; + } + } if (configuration["Prop6"] is string value6) { @@ -113,7 +121,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop6 = default; + instance.Prop6 = instance.Prop6; } if (configuration["Prop8"] is string value7) @@ -122,7 +130,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop8 = default; + instance.Prop8 = instance.Prop8; } if (configuration["Prop9"] is string value8) @@ -131,7 +139,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop9 = default; + instance.Prop9 = instance.Prop9; } if (configuration["Prop10"] is string value9) @@ -140,7 +148,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop10 = default; + instance.Prop10 = instance.Prop10; } if (configuration["Prop13"] is string value10) @@ -149,7 +157,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop13 = default; + instance.Prop13 = instance.Prop13; } if (configuration["Prop14"] is string value11) @@ -158,7 +166,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop14 = default; + instance.Prop14 = instance.Prop14; } if (configuration["Prop15"] is string value12) @@ -167,18 +175,34 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop15 = default; + instance.Prop15 = instance.Prop15; } if (configuration["Prop16"] is string value13) { instance.Prop16 = value13; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop16; + if (currentValue is not null) + { + instance.Prop16 = currentValue; + } + } if (configuration["Prop17"] is string value14) { instance.Prop17 = ParseSystemGlobalizationCultureInfo(value14, configuration.GetSection("Prop17").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop17; + if (currentValue is not null) + { + instance.Prop17 = currentValue; + } + } if (configuration["Prop19"] is string value15) { @@ -186,7 +210,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop19 = default; + instance.Prop19 = instance.Prop19; } if (configuration["Prop20"] is string value16) @@ -195,7 +219,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop20 = default; + instance.Prop20 = instance.Prop20; } if (configuration["Prop21"] is string value17) @@ -204,7 +228,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop21 = default; + instance.Prop21 = instance.Prop21; } if (configuration["Prop23"] is string value18) @@ -213,7 +237,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop23 = default; + instance.Prop23 = instance.Prop23; } if (configuration["Prop24"] is string value19) @@ -222,18 +246,34 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop24 = default; + instance.Prop24 = instance.Prop24; } if (configuration["Prop25"] is string value20) { instance.Prop25 = ParseSystemUri(value20, configuration.GetSection("Prop25").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop25; + if (currentValue is not null) + { + instance.Prop25 = currentValue; + } + } if (configuration["Prop26"] is string value21) { instance.Prop26 = ParseSystemVersion(value21, configuration.GetSection("Prop26").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop26; + if (currentValue is not null) + { + instance.Prop26 = currentValue; + } + } if (configuration["Prop27"] is string value22) { @@ -241,7 +281,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop27 = default; + instance.Prop27 = instance.Prop27; } if (configuration["Prop7"] is string value23) @@ -250,7 +290,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop7 = default; + instance.Prop7 = instance.Prop7; } if (configuration["Prop11"] is string value24) @@ -259,7 +299,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop11 = default; + instance.Prop11 = instance.Prop11; } if (configuration["Prop12"] is string value25) @@ -268,7 +308,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop12 = default; + instance.Prop12 = instance.Prop12; } if (configuration["Prop18"] is string value26) @@ -277,7 +317,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop18 = default; + instance.Prop18 = instance.Prop18; } if (configuration["Prop22"] is string value27) @@ -286,13 +326,21 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop22 = default; + instance.Prop22 = instance.Prop22; } if (configuration["Prop28"] is string value28) { instance.Prop28 = ParseByteArray(value28, configuration.GetSection("Prop28").Path); } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Prop28; + if (currentValue is not null) + { + instance.Prop28 = currentValue; + } + } if (configuration["Prop29"] is string value29) { @@ -300,7 +348,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop29 = default; + instance.Prop29 = instance.Prop29; } if (configuration["Prop30"] is string value30) @@ -309,7 +357,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Prop30 = default; + instance.Prop30 = instance.Prop30; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt index c7a8191aa6e94..1d34626861e9a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt @@ -106,7 +106,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -143,6 +143,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -150,7 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt index e7ebd66c7b8cf..1f016a7edea34 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -106,7 +106,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -143,6 +143,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -150,7 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt index 63fd389cb721a..36cf87364f313 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt @@ -106,7 +106,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -143,6 +143,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -150,7 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index b95cb9ae0e954..4dbf342fd26b0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -100,7 +100,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } } @@ -137,6 +137,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.MyString = value3; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.MyString; + if (currentValue is not null) + { + instance.MyString = currentValue; + } + } if (configuration["MyInt"] is string value4) { @@ -144,7 +152,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.MyInt = default; + instance.MyInt = instance.MyInt; } if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/UnsupportedTypes.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/UnsupportedTypes.generated.txt index 8becba5ac74ca..1803db583da84 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/UnsupportedTypes.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/UnsupportedTypes.generated.txt @@ -128,6 +128,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { instance.Name = value2; } + else if (defaultValueIfNotFound) + { + var currentValue = instance.Name; + if (currentValue is not null) + { + instance.Name = currentValue; + } + } if (configuration["Age"] is string value3) { @@ -135,7 +143,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } else if (defaultValueIfNotFound) { - instance.Age = default; + instance.Age = instance.Age; } if (AsConfigWithChildren(configuration.GetSection("List")) is IConfigurationSection section4) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Json/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Configuration.Json/src/PACKAGE.md index 825b3dbe412d4..705f7cbed7d4f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Json/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Configuration.Json/src/PACKAGE.md @@ -2,7 +2,7 @@ -JSON configuration provider implementation for [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/). This package enables you to read your application's settings from a JSON file. You can use [JsonConfigurationExtensions.AddJsonFile](https://docs.microsoft.com/dotnet/api/microsoft.extensions.configuration.jsonconfigurationextensions.addjsonfile) extension method on `IConfigurationBuilder` to add the JSON configuration provider to the configuration builder. +JSON configuration provider implementation for [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/). This package enables you to read your application's settings from a JSON file. You can use [JsonConfigurationExtensions.AddJsonFile](https://learn.microsoft.com/dotnet/api/microsoft.extensions.configuration.jsonconfigurationextensions.addjsonfile) extension method on `IConfigurationBuilder` to add the JSON configuration provider to the configuration builder. ## How to Use diff --git a/src/libraries/Microsoft.Extensions.Configuration/README.md b/src/libraries/Microsoft.Extensions.Configuration/README.md index 73c432729cf01..984eb1b85cd1c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/README.md +++ b/src/libraries/Microsoft.Extensions.Configuration/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Configuration` is combined with a core configuration abstraction under `Microsoft.Extensions.Configuration.Abstractions` that allows for building different kinds of configuration providers to retrieve key/value pair configuration values from in the form of `IConfiguration`. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/configuration. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../README.md#contribution-bar) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/README.md b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/README.md index 3d2f82f682b5b..2828a64dc4856 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.DependencyInjection.Abstractions` contains a core DI abstraction that allows for building different kinds of dependency injection containers to retrieve services from that have been registered with different lifetimes. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/dependency-injection. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/README.md b/src/libraries/Microsoft.Extensions.DependencyInjection/README.md index 38bd0d27f126b..d8439736491dc 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/README.md +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.DependencyInjection` is combined with a core DI abstraction under `Microsoft.Extensions.DependencyInjection.Abstractions` that allows for building different kinds of dependency injection containers to retrieve services from that have been registered with different lifetimes. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/dependency-injection. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.DependencyModel/src/PACKAGE.md index 19b13040a8aea..050207e31f22e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/PACKAGE.md @@ -4,7 +4,7 @@ Provides abstractions for reading `.deps` files. When a .NET application is compiled, the SDK generates a JSON manifest file (`.deps.json`) that contains information about application dependencies. You can use `Microsoft.Extensions.DependencyModel` to read information from this manifest at run time. This is useful when you want to dynamically compile code (for example, using Roslyn Emit API) referencing the same dependencies as your main application. -By default, the dependency manifest contains information about the application's target framework and runtime dependencies. Set the [PreserveCompilationContext](https://docs.microsoft.com/dotnet/core/project-sdk/msbuild-props#preservecompilationcontext) project property to `true` to additionally include information about reference assemblies used during compilation. +By default, the dependency manifest contains information about the application's target framework and runtime dependencies. Set the [PreserveCompilationContext](https://learn.microsoft.com/dotnet/core/project-sdk/msbuild-props#preservecompilationcontext) project property to `true` to additionally include information about reference assemblies used during compilation. ## How to Use @@ -39,8 +39,8 @@ class Program * [.deps.json file format](https://github.com/dotnet/sdk/blob/main/documentation/specs/runtime-configuration-file.md#appnamedepsjson) -* [Microsoft.Extensions.DependencyModel namespace](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencymodel) -* [Microsoft.Extensions.DependencyModel.DependencyContext](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencymodel.dependencycontext) +* [Microsoft.Extensions.DependencyModel namespace](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencymodel) +* [Microsoft.Extensions.DependencyModel.DependencyContext](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencymodel.dependencycontext) ## Feedback & Contributing diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md index 200587369cc7b..5c4689bb7dca0 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md @@ -10,7 +10,7 @@ Commonly Used Types: - `Microsoft.Extensions.Diagnostics.Metrics.MetricsBuilderExtensions` - `Microsoft.Extensions.Diagnostics.Metrics.MetricsOptions` -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/diagnostics. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/diagnostics. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index dd1be2d1512d3..ca1ca5f7802e4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.Metrics; @@ -40,7 +41,7 @@ public Meter Create(MeterOptions options) { foreach (Meter meter in meterList) { - if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as List>, options.Tags)) + if (meter.Version == options.Version && DiagnosticsHelper.CompareTags(meter.Tags as IList>, options.Tags)) { return meter; } diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/README.md index 923206b1c4f0d..742d88c094e7e 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/README.md @@ -6,7 +6,7 @@ Hosting provides as a primitive the concept of a hosted service, which is how ap Hosting provides good integration for long-running console applications, windows services, ASP.NET Core. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/generic-host. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs index d752c024cbed7..49e5e64a968af 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs @@ -29,7 +29,7 @@ public abstract class BackgroundService : IHostedService, IDisposable /// /// Triggered when is called. /// A that represents the long running operations. - /// See Worker Services in .NET for implementation guidelines. + /// See Worker Services in .NET for implementation guidelines. protected abstract Task ExecuteAsync(CancellationToken stoppingToken); /// diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/README.md b/src/libraries/Microsoft.Extensions.Hosting.Systemd/README.md index 0d9d5b59cce61..ba20c1a17e6d9 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/README.md +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Hosting.Systemd` contains implementation for using hosting in Systemd Services. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/generic-host. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/README.md b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/README.md index fad810de55a74..4ec180825f956 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/README.md +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Hosting.WindowsServices` contains implementation for using hosting in Windows Services. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting. +Documentation can be found at https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) diff --git a/src/libraries/Microsoft.Extensions.Hosting/README.md b/src/libraries/Microsoft.Extensions.Hosting/README.md index 9aa05bc8b2a71..277f5a3bb770f 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/README.md +++ b/src/libraries/Microsoft.Extensions.Hosting/README.md @@ -6,7 +6,7 @@ Hosting provides as a primitive the concept of a hosted service, which is how ap Hosting provides good integration for long-running console applications, windows services, ASP.NET Core. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/generic-host. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md b/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md index 870e5d0b53484..947f602e29eab 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md +++ b/src/libraries/Microsoft.Extensions.Hosting/docs/HostShutdown.md @@ -47,7 +47,7 @@ has logic to handle scenario (5) above. Apps that call `Environment.Exit`, and n If your application uses Hosting, and you want to gracefully stop the host, you can call [`IHostApplicationLifetime.StopApplication()`][StopApplication] instead of `Environment.Exit`. -[StopApplication]: https://docs.microsoft.com/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.stopapplication +[StopApplication]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime.stopapplication ### Hosting Shutdown Process diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs index 21b39a15b121e..fa154227f7c67 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs @@ -5,18 +5,22 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net.Http; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Http { internal sealed class DefaultHttpMessageHandlerBuilder : HttpMessageHandlerBuilder { + private HttpMessageHandler? _primaryHandler; + private string? _name; + public DefaultHttpMessageHandlerBuilder(IServiceProvider services) { Services = services; } - private string? _name; - [DisallowNull] public override string? Name { @@ -28,7 +32,11 @@ public override string? Name } } - public override HttpMessageHandler PrimaryHandler { get; set; } = new HttpClientHandler(); + public override HttpMessageHandler PrimaryHandler + { + get => _primaryHandler ??= CreatePrimaryHandler(); + set => _primaryHandler = value; + } public override IList AdditionalHandlers { get; } = new List(); @@ -44,5 +52,36 @@ public override HttpMessageHandler Build() return CreateHandlerPipeline(PrimaryHandler, AdditionalHandlers); } + +#pragma warning disable CA1822, CA1859 // Mark members as static, Use concrete types when possible for improved performance + private HttpMessageHandler CreatePrimaryHandler() +#pragma warning restore CA1822, CA1859 + { +#if NET + // On platforms where SocketsHttpHandler is supported, HttpClientHandler is a thin wrapper + // around it. By using SocketsHttpHandler directly, we can avoid the overhead of the wrapper, + // but more importantly, we can configure it to limit the lifetime of its pooled connections + // to match the requested lifetime of the handler itself. That way, if/when someone holds on + // to a resulting HttpClient for a prolonged period of time, it'll still benefit from connection + // recycling, and without needing to tear down and reconstitute the rest of the handler pipeline. + if (SocketsHttpHandler.IsSupported) + { + SocketsHttpHandler handler = new(); + + if (Services.GetService>() is IOptionsMonitor optionsMonitor) + { + TimeSpan lifetime = optionsMonitor.Get(_name).HandlerLifetime; + if (lifetime >= TimeSpan.Zero) + { + handler.PooledConnectionLifetime = lifetime; + } + } + + return handler; + } +#endif + + return new HttpClientHandler(); + } } } diff --git a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpMessageHandlerBuilderTest.cs b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpMessageHandlerBuilderTest.cs index a597114d2f4e2..acc21dc6a101f 100644 --- a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpMessageHandlerBuilderTest.cs +++ b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpMessageHandlerBuilderTest.cs @@ -28,7 +28,16 @@ public void Ctor_SetsPrimaryHandler() var builder = new DefaultHttpMessageHandlerBuilder(Services); // Act - Assert.IsType(builder.PrimaryHandler); +#if NET + if (SocketsHttpHandler.IsSupported) + { + Assert.IsType(builder.PrimaryHandler); + } + else +#endif + { + Assert.IsType(builder.PrimaryHandler); + } } // Moq heavily utilizes RefEmit, which does not work on most aot workloads @@ -78,7 +87,7 @@ public void Build_SomeAdditionalHandlers_PutsTogetherDelegatingHandlers() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/50873", TestPlatforms.Android)] - public void Build_PrimaryHandlerIsNull_ThrowsException() + public void Build_PrimaryHandlerIsNull_UsesDefault() { // Arrange var builder = new DefaultHttpMessageHandlerBuilder(Services) @@ -87,8 +96,8 @@ public void Build_PrimaryHandlerIsNull_ThrowsException() }; // Act & Assert - var exception = Assert.Throws(() => builder.Build()); - Assert.Equal("The 'PrimaryHandler' must not be null.", exception.Message); + var handler = builder.Build(); + Assert.NotNull(handler); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/SocketsHttpHandlerConfigurationTest.cs b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/SocketsHttpHandlerConfigurationTest.cs index 059557a0784a7..017a6b44aa486 100644 --- a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/SocketsHttpHandlerConfigurationTest.cs +++ b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/SocketsHttpHandlerConfigurationTest.cs @@ -42,10 +42,41 @@ public void UseSocketsHttpHandler_Parameterless_Success() var defaultPrimaryHandlerChain = messageHandlerFactory.CreateHandler("DefaultPrimaryHandler"); var socketsHttpHandlerChain = messageHandlerFactory.CreateHandler("SocketsHttpHandler"); - Assert.IsType(GetPrimaryHandler(defaultPrimaryHandlerChain)); + Assert.IsType(GetPrimaryHandler(defaultPrimaryHandlerChain)); Assert.IsType(GetPrimaryHandler(socketsHttpHandlerChain)); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void DefaultPrimaryHandler_RespectsHandlerLifetime() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddHttpClient(); + serviceCollection.Configure(options => options.HandlerLifetime = TimeSpan.FromMinutes(42)); + + var services = serviceCollection.BuildServiceProvider(); + var messageHandlerFactory = services.GetRequiredService(); + var defaultPrimaryHandlerChain = messageHandlerFactory.CreateHandler(); + + SocketsHttpHandler handler = Assert.IsType(GetPrimaryHandler(defaultPrimaryHandlerChain)); + Assert.Equal(TimeSpan.FromMinutes(42), handler.PooledConnectionLifetime); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void DefaultPrimaryHandler_NamedClient_RespectsHandlerLifetime() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddHttpClient("Configured"); + serviceCollection.Configure("Configured1", options => options.HandlerLifetime = TimeSpan.FromMinutes(42)); + serviceCollection.Configure("Configured2", options => options.HandlerLifetime = TimeSpan.FromMinutes(84)); + + var services = serviceCollection.BuildServiceProvider(); + var messageHandlerFactory = services.GetRequiredService(); + + Assert.Equal(TimeSpan.FromMinutes(42), Assert.IsType(GetPrimaryHandler(messageHandlerFactory.CreateHandler("Configured1"))).PooledConnectionLifetime); + Assert.Equal(TimeSpan.FromMinutes(84), Assert.IsType(GetPrimaryHandler(messageHandlerFactory.CreateHandler("Configured2"))).PooledConnectionLifetime); + Assert.Equal(TimeSpan.FromMinutes(2), Assert.IsType(GetPrimaryHandler(messageHandlerFactory.CreateHandler())).PooledConnectionLifetime); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] public void UseSocketsHttpHandler_ConfiguredByAction_Success() { @@ -66,7 +97,7 @@ public void UseSocketsHttpHandler_ConfiguredByAction_Success() var unconfiguredHandler = (SocketsHttpHandler)GetPrimaryHandler(unconfiguredHandlerChain); var configuredHandler = (SocketsHttpHandler)GetPrimaryHandler(configuredHandlerChain); - Assert.Equal(Timeout.InfiniteTimeSpan, unconfiguredHandler.PooledConnectionLifetime); + Assert.Equal(TimeSpan.FromMinutes(2), unconfiguredHandler.PooledConnectionLifetime); Assert.Equal(TimeSpan.FromMinutes(1), configuredHandler.PooledConnectionLifetime); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/Microsoft.Extensions.Logging.Abstractions.sln b/src/libraries/Microsoft.Extensions.Logging.Abstractions/Microsoft.Extensions.Logging.Abstractions.sln index 4064dd66cbf19..27d405dd7a6cb 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/Microsoft.Extensions.Logging.Abstractions.sln +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/Microsoft.Extensions.Logging.Abstractions.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Loggin EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests.csproj", "{1CB869A7-2EEC-4A53-9C33-DF9E0C75825B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests.csproj", "{D6167506-0671-46A3-94E5-7A98032DCEC6}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{852D4E16-58C3-47C2-A6BC-A5B12B37209F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{6645D0C4-83D1-4426-B9CD-67096CB7A60F}" @@ -135,6 +137,10 @@ Global {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.Build.0 = Release|Any CPU + {D6167506-0671-46A3-94E5-7A98032DCEC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6167506-0671-46A3-94E5-7A98032DCEC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6167506-0671-46A3-94E5-7A98032DCEC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6167506-0671-46A3-94E5-7A98032DCEC6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -162,6 +168,7 @@ Global {8215F79E-510B-4CA1-B775-50C47BB58360} = {58760833-B4F5-429D-9ABD-15FDF83E25CD} {F3186815-B9A5-455F-B0DF-E39D4235C24F} = {14DFA192-3C7E-4F10-B5FD-3953BC82A6B1} {14DFA192-3C7E-4F10-B5FD-3953BC82A6B1} = {58760833-B4F5-429D-9ABD-15FDF83E25CD} + {D6167506-0671-46A3-94E5-7A98032DCEC6} = {4DE63935-DCA9-4D63-9C1F-AAE79C89CA8B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {450DA749-CBDC-4BDC-950F-8A491CF59D49} diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Logging.Abstractions/README.md index e0b0a1710d498..cc89e7b525c90 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/README.md @@ -11,7 +11,7 @@ Commonly Used Types: - `Microsoft.Extensions.Logging.LoggerMessage` - `Microsoft.Extensions.Logging.Abstractions.NullLogger` -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/DiagnosticDescriptors.cs index 409ab6de3f51c..a8f2af180720a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/DiagnosticDescriptors.cs @@ -208,5 +208,13 @@ public static class DiagnosticDescriptors category: "LoggingGenerator", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor PrimaryConstructorParameterLoggerHidden { get; } = DiagnosticDescriptorHelper.Create( + id: "SYSLIB1027", + title: new LocalizableResourceString(nameof(SR.PrimaryConstructorParameterLoggerHiddenTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.PrimaryConstructorParameterLoggerHiddenMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + category: "LoggingGenerator", + DiagnosticSeverity.Info, + isEnabledByDefault: true); } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index 1604b8689d97c..b474c5ea54a15 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -630,11 +630,23 @@ private static string GenerateClassName(TypeDeclarationSyntax typeDeclaration) INamedTypeSymbol? classType = sm.GetDeclaredSymbol(classDec, _cancellationToken); + INamedTypeSymbol? currentClassType = classType; bool onMostDerivedType = true; - while (classType is { SpecialType: not SpecialType.System_Object }) + // We keep track of the names of all non-logger fields, since they prevent referring to logger + // primary constructor parameters with the same name. Example: + // partial class C(ILogger logger) + // { + // private readonly object logger = logger; + // + // [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + // public partial void M1(); // The ILogger primary constructor parameter cannot be used here. + // } + HashSet shadowedNames = new(StringComparer.Ordinal); + + while (currentClassType is { SpecialType: not SpecialType.System_Object }) { - foreach (IFieldSymbol fs in classType.GetMembers().OfType()) + foreach (IFieldSymbol fs in currentClassType.GetMembers().OfType()) { if (!onMostDerivedType && fs.DeclaredAccessibility == Accessibility.Private) { @@ -651,10 +663,52 @@ private static string GenerateClassName(TypeDeclarationSyntax typeDeclaration) return (null, true); } } + else + { + shadowedNames.Add(fs.Name); + } } onMostDerivedType = false; - classType = classType.BaseType; + currentClassType = currentClassType.BaseType; + } + + // We prioritize fields over primary constructor parameters and avoid warnings if both exist. + if (loggerField is not null) + { + return (loggerField, false); + } + + IEnumerable primaryConstructors = classType.InstanceConstructors + .Where(ic => ic.DeclaringSyntaxReferences + .Any(ds => ds.GetSyntax() is ClassDeclarationSyntax)); + + foreach (IMethodSymbol primaryConstructor in primaryConstructors) + { + foreach (IParameterSymbol parameter in primaryConstructor.Parameters) + { + if (IsBaseOrIdentity(parameter.Type, loggerSymbol)) + { + if (shadowedNames.Contains(parameter.Name)) + { + // Accessible fields always shadow primary constructor parameters, + // so we can't use the primary constructor parameter, + // even if the field is not a valid logger. + Diag(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden, parameter.Locations[0], classDec.Identifier.Text); + + continue; + } + + if (loggerField == null) + { + loggerField = parameter.Name; + } + else + { + return (null, true); + } + } + } } return (loggerField, false); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs index 7dd80ba2926bf..74cc8e13fcf0e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs @@ -45,7 +45,7 @@ private static void Execute(Compilation compilation, ImmutableArray distinctClasses = classes.Distinct(); + ImmutableHashSet distinctClasses = classes.ToImmutableHashSet(); var p = new Parser(compilation, context.ReportDiagnostic, context.CancellationToken); IReadOnlyList logClasses = p.GetLogClasses(distinctClasses); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/Strings.resx index 602e6f4730d21..cca9ce4ef6c0e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/Strings.resx @@ -1,17 +1,17 @@ - @@ -237,4 +237,12 @@ The Logging source generator is not available in C# {0}. Please use language version {1} or greater. + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf index 6f3218ac6dd5b..eb8dd19b8c71a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.cs.xlf @@ -132,6 +132,16 @@ Našlo se několik polí typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Odeberte redundantní kvalifikátor (Informace:, Upozornění:, Chyba: atd.) ze zprávy o protokolování, protože je na zadané úrovni protokolu implicitní. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf index b838765022a4e..b508628ac5ec4 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.de.xlf @@ -132,6 +132,16 @@ Mehrere Felder vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Entfernen Sie den redundanten Qualifizierer (z. B. "Info:", "Warnung:" oder "Fehler:") aus der Protokollierungsmeldung, weil er auf der angegebenen Protokollebene implizit enthalten ist. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf index 30000484775d0..fd199f459d96a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.es.xlf @@ -132,6 +132,16 @@ Se encontraron varios campos de tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Quitar calificadores redundantes (Información:, Advertencia:, Error:, etc.) del mensaje de registro, ya que está implícito en el nivel de registro especificado. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf index 04ccccc8536aa..ac019c9dabf7c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.fr.xlf @@ -132,6 +132,16 @@ Plusieurs champs de type Microsoft.Extensions.Logging.ILogger ont été trouvés {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Supprimez le qualificateur redondant (Info:, Warning:, Error:, etc.) du message de journalisation, car il est implicite dans le niveau de journalisation spécifié. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf index 0c007fdce5482..dc2f02184e1b6 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.it.xlf @@ -132,6 +132,16 @@ Sono stati trovati più campi di tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Rimuovere il qualificatore ridondante (Informazioni:, Avviso:, Errore: e così via) dal messaggio di registrazione perché è implicito nel livello di log specificato. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf index 36f5b1baad528..3f0127050182c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ja.xlf @@ -132,6 +132,16 @@ Microsoft.Extensions.Logging.ILogger という種類の複数のフィールドが見つかりました {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. 指定されたログ レベルでは暗黙的であるため、冗長な修飾子 (Info:、Warning:、Error: など) をログ メッセージから削除します。 diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf index 547c49b3e33dc..5bb9507b8591c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ko.xlf @@ -132,6 +132,16 @@ Microsoft.Extensions.Logging.ILogger 형식의 필드가 여러 개 있음 {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. 중복 한정자(정보:, 경고:, 오류: 등)가 지정된 로그 수준에서 암시적이기 때문에 로깅 메시지에서 제거합니다. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf index a40b72d24ffce..0c014dd5317e0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pl.xlf @@ -132,6 +132,16 @@ Znaleziono wiele pól typu Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Usuń nadmiarowy kwalifikator (Info:, Warning:, Error: itp.) z komunikatu rejestrowania, ponieważ jest on domyślny na określonym poziomie dziennika. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf index 0560fb6b5907c..6a4a35d1acc52 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.pt-BR.xlf @@ -132,6 +132,16 @@ Múltiplos campos encontrados do tipo Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Remova o qualificador redundante (Info:, Aviso:, Erro:, etc) da mensagem de log, pois está implícito no nível de log especificado. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf index 589635d5721bb..053970d888603 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.ru.xlf @@ -132,6 +132,16 @@ Обнаружено несколько полей типа Microsoft.Extensions.Logging.ILogger {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Удалите избыточный квалификатор (Info:, Warning:, Error:, и т. п.) из сообщения журнала, поскольку квалификатор подразумевается на указанном уровне ведения журнала. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf index c40d99aa4602c..03f094a160738 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.tr.xlf @@ -132,6 +132,16 @@ Microsoft.Extensions.Logging.ILogger türünde birden çok alan bulundu {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. Belirtilen günlük düzeyinde örtük olduğundan gereksiz niteleyiciyi (Bilgi:, Uyarı:, Hata: vb.) günlüğe kaydetme iletisinden kaldırın. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf index 04c39c8843a1e..2217d0a4cc988 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -132,6 +132,16 @@ 找到 Microsoft.Extensions.Logging.ILogger 类型的多个字段 {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. 从日志记录消息中删除冗余限定符(信息:、警告:、错误: 等),因为其在指定的日志级别中为隐式内容。 diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf index af008cf098ff8..6044c601d9251 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -132,6 +132,16 @@ 找到多個 Microsoft.Extensions.Logging.ILogger 類型的欄位 {Locked="Microsoft.Extensions.Logging.ILogger"} + + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use + {Locked="Microsoft.Extensions.Logging.ILogger"} + + + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field + {Locked="Microsoft.Extensions.Logging.ILogger"} + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. 從記錄訊息中移除備援限定詞 (資訊:、警告:、錯誤: 等等),因為它在指定的記錄層級中為隱含。 diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructor.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructor.generated.txt new file mode 100644 index 0000000000000..ba6b0d865e6bf --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructor.generated.txt @@ -0,0 +1,21 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithLoggerFromPrimaryConstructor + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + public partial void M0() + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug)) + { + __M0Callback(logger, null); + } + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerInFieldAndFromPrimaryConstructor.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerInFieldAndFromPrimaryConstructor.generated.txt new file mode 100644 index 0000000000000..acfd43a0d287b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerInFieldAndFromPrimaryConstructor.generated.txt @@ -0,0 +1,21 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithLoggerFromPrimaryConstructor + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + public partial void M0() + { + if (_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug)) + { + __M0Callback(_logger, null); + } + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 7688e26ad0062..1013af8ae3672 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -26,7 +26,7 @@ public void FindsLoggerFieldInBaseClass() } [Fact] - public void FindsLoggerFieldInAnotherParialClass() + public void FindsLoggerFieldInAnotherPartialClass() { var logger = new MockLogger(); @@ -36,6 +36,41 @@ public void FindsLoggerFieldInAnotherParialClass() Assert.Equal("Test.", logger.LastFormattedString); } +#if ROSLYN4_8_OR_GREATER + [Fact] + public void FindsLoggerInPrimaryConstructorParameter() + { + var logger = new MockLogger(); + + logger.Reset(); + + new ClassWithPrimaryConstructor(logger).Test(); + Assert.Equal("Test.", logger.LastFormattedString); + } + + [Fact] + public void FindsLoggerInPrimaryConstructorParameterInDifferentPartialDeclaration() + { + var logger = new MockLogger(); + + logger.Reset(); + + new ClassWithPrimaryConstructorInDifferentPartialDeclaration(logger).Test(); + Assert.Equal("Test.", logger.LastFormattedString); + } + + [Fact] + public void FindsLoggerInFieldInitializedFromPrimaryConstructorParameter() + { + var logger = new MockLogger(); + + logger.Reset(); + + new ClassWithPrimaryConstructor(logger).Test(); + Assert.Equal("Test.", logger.LastFormattedString); + } +#endif + [Fact] public void BasicTests() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 30cf036fdd860..0952f66a8adab 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -187,6 +187,40 @@ public sealed class BarAttribute : Attribute { } await VerifyAgainstBaselineUsingFile("TestWithNestedClassWithGenericTypesWithAttributes.generated.txt", testSourceCode); } +#if ROSLYN4_8_OR_GREATER + [Fact] + public async Task TestBaseline_TestWithLoggerFromPrimaryConstructor_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal partial class TestWithLoggerFromPrimaryConstructor(ILogger logger) + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M0"")] + public partial void M0(); + } +}"; + await VerifyAgainstBaselineUsingFile("TestWithLoggerFromPrimaryConstructor.generated.txt", testSourceCode); + } + + [Fact] + public async Task TestBaseline_TestWithLoggerInFieldAndFromPrimaryConstructor_UsesField() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal partial class TestWithLoggerFromPrimaryConstructor(ILogger logger) + { + private readonly ILogger _logger = logger; + + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M0"")] + public partial void M0(); + } +}"; + await VerifyAgainstBaselineUsingFile("TestWithLoggerInFieldAndFromPrimaryConstructor.generated.txt", testSourceCode); + } +#endif + [Fact] public void GenericTypeParameterAttributesAreRetained() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index d0d5fc30af573..15a4f96507477 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -417,6 +417,142 @@ internal partial class Logger } #endif + [Fact] + public async Task FieldOnOtherPartialDeclarationOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + private ILogger _logger; + + public C(ILogger logger) + { + _logger = logger; + } + } + + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Empty(diagnostics); + } + +#if ROSLYN4_8_OR_GREATER + [Fact] + public async Task PrimaryConstructorOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger) + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task PrimaryConstructorOnOtherPartialDeclarationOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger); + + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task PrimaryConstructorWithDifferentNameLoggerFieldOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger) + { + private readonly ILogger _logger = logger; + + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task PrimaryConstructorWithSameNameLoggerFieldOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger) + { + private readonly ILogger logger = logger; + + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Empty(diagnostics); + } + + [Fact] + public async Task PrimaryConstructorLoggerShadowedByField() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger) + { + private readonly object logger = logger; + + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Equal(2, diagnostics.Count); + + Assert.Equal(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden.Id, diagnostics[0].Id); + var lineSpan = diagnostics[0].Location.GetLineSpan(); + Assert.Equal(4, lineSpan.StartLinePosition.Line); + Assert.Equal(40, lineSpan.StartLinePosition.Character); + + Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[1].Id); + } + + [Fact] + public async Task PrimaryConstructorLoggerShadowedByBaseClass() + { + IReadOnlyList diagnostics = await RunGenerator(@" + class Base(object logger) { + protected readonly object logger = logger; + } + + partial class Derived(ILogger logger) : Base(logger) + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + } + "); + + Assert.Equal(2, diagnostics.Count); + + Assert.Equal(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden.Id, diagnostics[0].Id); + var lineSpan = diagnostics[0].Location.GetLineSpan(); + Assert.Equal(8, lineSpan.StartLinePosition.Line); + Assert.Equal(46, lineSpan.StartLinePosition.Character); + + Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[1].Id); + } +#endif + [Theory] [InlineData("false")] [InlineData("true")] diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests.csproj new file mode 100644 index 0000000000000..e480e542b1544 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests.csproj @@ -0,0 +1,22 @@ + + + + $(MicrosoftCodeAnalysisVersion_4_8) + $(DefineConstants);ROSLYN4_0_OR_GREATER;ROSLYN4_8_OR_GREATER + true + -O1 + + false + + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index 05984d67570bc..5f4fec36ac7b7 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -57,6 +57,30 @@ public partial class PartialClassWithLoggerField public partial void Test(); } +#if ROSLYN4_8_OR_GREATER +public partial class ClassWithPrimaryConstructor(ILogger logger) +{ + [LoggerMessage(0, LogLevel.Debug, "Test.")] + public partial void Test(); +} + +public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration(ILogger logger); + +public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration +{ + [LoggerMessage(0, LogLevel.Debug, "Test.")] + public partial void Test(); +} + +public partial class ClassWithPrimaryConstructorAndField(ILogger logger) +{ + private readonly ILogger _logger = logger; + + [LoggerMessage(0, LogLevel.Debug, "Test.")] + public partial void Test(); +} +#endif + // Used to test use outside of a namespace internal static partial class NoNamespace { diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/README.md b/src/libraries/Microsoft.Extensions.Logging.Configuration/README.md index f22c46c7d4fe9..3767fb4c798ad 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging.Configuration` provides configuration support for Microsoft.Extensions.Logging. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/README.md b/src/libraries/Microsoft.Extensions.Logging.Console/README.md index dda428b5af418..ca8c1b83226df 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.Console/README.md @@ -2,11 +2,11 @@ `Microsoft.Extensions.Logging.Console` provides a Console logger provider implementation for Microsoft.Extensions.Logging. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. -To learn about our .NET 5 feature on how to control the format of console logs check out [this documentation](https://learn.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter). +To learn about our .NET 5 feature on how to control the format of console logs check out [this documentation](https://learn.microsoft.com/dotnet/core/extensions/console-log-formatter). -To learn about our .NET 6 feature on how to use a source generator to create log messages check out [this documentation](https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator). +To learn about our .NET 6 feature on how to use a source generator to create log messages check out [this documentation](https://learn.microsoft.com/dotnet/core/extensions/logger-message-generator). ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.Debug/README.md b/src/libraries/Microsoft.Extensions.Logging.Debug/README.md index d92d06e2e99b8..5ca2f862f6f6e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Debug/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.Debug/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging.Debug` provides a Debug output logger provider implementation for Microsoft.Extensions.Logging. This logger logs messages to a debugger monitor by writing messages with `System.Diagnostics.Debug.WriteLine()`. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.Debug/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.Debug/src/PACKAGE.md index 97b378f4350c5..b9908263d4b48 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Debug/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.Debug/src/PACKAGE.md @@ -8,7 +8,7 @@ * Allow logging to the debugger output. -* Provide extensions method for the [ILoggingBuilder](https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggingbuilder) class to easily enable this Debug logger. +* Provide extensions method for the [ILoggingBuilder](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggingbuilder) class to easily enable this Debug logger. ## How to Use diff --git a/src/libraries/Microsoft.Extensions.Logging.EventLog/README.md b/src/libraries/Microsoft.Extensions.Logging.EventLog/README.md index fd0c336e14770..9ec3bd8eaeed3 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventLog/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.EventLog/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging.EventLog` provides a Windows Event Log logger provider implementation for Microsoft.Extensions.Logging. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/README.md b/src/libraries/Microsoft.Extensions.Logging.EventSource/README.md index 7d806bfc5d2f3..8727a91e15f07 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging.EventSource` provides a basic implementation for the built-in event source logger provider. Using `Microsoft.Extensions.Logging.EventSource.LoggingEventSource` which is the bridge from all ILogger-based logging to EventSource/EventListener logging, logging can be enabled by enabling the event source called "Microsoft-Extensions-Logging". -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/README.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/README.md index a4edcaef0429a..e1bca63c3a632 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/README.md +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging.TraceSource` provides a basic implementation for the built-in TraceSource logger provider. This logger logs messages to a trace listener by writing messages with `System.Diagnostics.TraceSource.TraceEvent()`. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Example diff --git a/src/libraries/Microsoft.Extensions.Logging/README.md b/src/libraries/Microsoft.Extensions.Logging/README.md index fc3ba6e48fd15..0d333e06c0616 100644 --- a/src/libraries/Microsoft.Extensions.Logging/README.md +++ b/src/libraries/Microsoft.Extensions.Logging/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Logging` is combined with a core logging abstraction under `Microsoft.Extensions.Logging.Abstractions`. This abstraction is available in our basic built-in implementations like console, event log, and debug (Debug.WriteLine). Also note, there is no dedicated built-in solution for file-based logging. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/logging. ## Examples @@ -60,7 +60,7 @@ public class LoggingSample2 } ``` -To reach a balance between performance and usability we added the compile-time logging source generator feature in .NET 6, to learn more about it and learn how to use a source generator to create log messages check out [this documentation](https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator). +To reach a balance between performance and usability we added the compile-time logging source generator feature in .NET 6, to learn more about it and learn how to use a source generator to create log messages check out [this documentation](https://learn.microsoft.com/dotnet/core/extensions/logger-message-generator). ### Baggage and Tags for `ActivityTrackingOptions` diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/README.md b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/README.md index 91266396fd219..9f093b4aaf5fb 100644 --- a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/README.md +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Options.ConfigurationExtensions` provides additional configuration-specific functionality related to Options. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/options. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/options. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/README.md b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/README.md index 2e5b390bc9e3e..87cb759b958a2 100644 --- a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/README.md +++ b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Options.DataAnnotations` provides additional DataAnnotations specific functionality related to Options.. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/options. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/options. ## Example diff --git a/src/libraries/Microsoft.Extensions.Options/README.md b/src/libraries/Microsoft.Extensions.Options/README.md index 58f4ffa7d702f..d15cf5eef60de 100644 --- a/src/libraries/Microsoft.Extensions.Options/README.md +++ b/src/libraries/Microsoft.Extensions.Options/README.md @@ -2,7 +2,7 @@ `Microsoft.Extensions.Options` provides a strongly typed way of specifying and accessing settings using dependency injection and acts as a bridge between configuration, DI, and higher level libraries. This library is the glue for how an app developer uses DI to configure the behavior of a library like HttpClient Factory. This also enables user to get a strongly-typed view of their configuration. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/options. +Documentation can be found at https://learn.microsoft.com/dotnet/core/extensions/options. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/Microsoft.Extensions.Options/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Extensions.Options/src/CompatibilitySuppressions.xml index 0c096372f5d41..1142c9e21c495 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/CompatibilitySuppressions.xml +++ b/src/libraries/Microsoft.Extensions.Options/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@ - + CP0015 diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Extensions/EmptyReadonlyDictionary.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Extensions/EmptyReadonlyDictionary.cs index 99ef907611280..f8b4f74593b99 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Extensions/EmptyReadonlyDictionary.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Extensions/EmptyReadonlyDictionary.cs @@ -38,7 +38,7 @@ TValue IDictionary.this[TKey key] public bool TryGetValue(TKey key, out TValue value) { -#pragma warning disable CS8601 // The recommended implementation: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue +#pragma warning disable CS8601 // The recommended implementation: https://learn.microsoft.com/dotnet/api/system.collections.generic.dictionary-2.trygetvalue value = default; #pragma warning restore diff --git a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/CompatibilitySuppressions.xml index cbecb005c817a..8af156c876426 100644 --- a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/CompatibilitySuppressions.xml +++ b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + PKV006 diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.VisualBasic.Core/src/CompatibilitySuppressions.xml index b4a619b5ccef1..721870b548747 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/src/CompatibilitySuppressions.xml +++ b/src/libraries/Microsoft.VisualBasic.Core/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0014 diff --git a/src/libraries/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistryRights.cs b/src/libraries/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistryRights.cs index faea51e71fd78..33a7bdc198ab7 100644 --- a/src/libraries/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistryRights.cs +++ b/src/libraries/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistryRights.cs @@ -5,7 +5,7 @@ namespace System.Security.AccessControl { // We derived this enum from the definitions of KEY_READ and such from // winnt.h and from MSDN, plus some experimental validation with regedit. - // https://docs.microsoft.com/en-us/windows/desktop/SysInfo/registry-key-security-and-access-rights + // https://learn.microsoft.com/windows/desktop/SysInfo/registry-key-security-and-access-rights [Flags] public enum RegistryRights { diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Win32.SystemEvents/src/CompatibilitySuppressions.xml index 639d29998aa60..038640c945743 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/CompatibilitySuppressions.xml +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/README.md b/src/libraries/README.md index e56a1f1272157..2688718a0270b 100644 --- a/src/libraries/README.md +++ b/src/libraries/README.md @@ -35,6 +35,6 @@ Some libraries are under more active development than others. Depending on the l ## Deployment -Some libraries are included in the .NET SDK as part of the runtime's [shared framework](https://learn.microsoft.com/en-us/dotnet/standard/glossary#shared-framework). Other libraries are deployed as out-of-band (OOB) NuGet packages and need to be installed separately. +Some libraries are included in the .NET SDK as part of the runtime's [shared framework](https://learn.microsoft.com/dotnet/standard/glossary#shared-framework). Other libraries are deployed as out-of-band (OOB) NuGet packages and need to be installed separately. -For more information, see the [Runtime libraries overview](https://learn.microsoft.com/en-us/dotnet/standard/runtime-libraries-overview). +For more information, see the [Runtime libraries overview](https://learn.microsoft.com/dotnet/standard/runtime-libraries-overview). diff --git a/src/libraries/System.CodeDom/src/CompatibilitySuppressions.xml b/src/libraries/System.CodeDom/src/CompatibilitySuppressions.xml index 03f406bb3550f..71d7d4720735a 100644 --- a/src/libraries/System.CodeDom/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.CodeDom/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/System.Collections.Concurrent/README.md b/src/libraries/System.Collections.Concurrent/README.md index 7faf36d5552ff..4d63322f3d7b0 100644 --- a/src/libraries/System.Collections.Concurrent/README.md +++ b/src/libraries/System.Collections.Concurrent/README.md @@ -1,7 +1,7 @@ # System.Collections.Concurrent This is the assembly that surfaces concurrent collections such as [`ConcurrentDictionary`](https://learn.microsoft.com/dotnet/api/system.collections.concurrent.concurrentdictionary-2) and [`ConcurrentBag`](https://learn.microsoft.com/dotnet/api/system.collections.concurrent.concurrentbag-1). It provides thread-safe collections that should be used whenever multiple threads are accessing the collection concurrently. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.collections.concurrent. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Collections.Concurrent/ref/System.Collections.Concurrent.cs b/src/libraries/System.Collections.Concurrent/ref/System.Collections.Concurrent.cs index 6c2b09321b7f9..5bfe38d5619a2 100644 --- a/src/libraries/System.Collections.Concurrent/ref/System.Collections.Concurrent.cs +++ b/src/libraries/System.Collections.Concurrent/ref/System.Collections.Concurrent.cs @@ -101,13 +101,14 @@ public ConcurrentDictionary(int concurrencyLevel, int capacity, System.Collectio public System.Collections.Generic.ICollection Values { get { throw null; } } public TValue AddOrUpdate(TKey key, System.Func addValueFactory, System.Func updateValueFactory) { throw null; } public TValue AddOrUpdate(TKey key, TValue addValue, System.Func updateValueFactory) { throw null; } - public TValue AddOrUpdate(TKey key, System.Func addValueFactory, System.Func updateValueFactory, TArg factoryArgument) { throw null; } + public TValue AddOrUpdate(TKey key, System.Func addValueFactory, System.Func updateValueFactory, TArg factoryArgument) where TArg : allows ref struct { throw null; } public void Clear() { } public bool ContainsKey(TKey key) { throw null; } + public AlternateLookup GetAlternateLookup() where TAlternateKey : notnull, allows ref struct { throw null; } public System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } public TValue GetOrAdd(TKey key, System.Func valueFactory) { throw null; } public TValue GetOrAdd(TKey key, TValue value) { throw null; } - public TValue GetOrAdd(TKey key, System.Func valueFactory, TArg factoryArgument) { throw null; } + public TValue GetOrAdd(TKey key, System.Func valueFactory, TArg factoryArgument) where TArg : allows ref struct { throw null; } void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair keyValuePair) { } bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair keyValuePair) { throw null; } void System.Collections.Generic.ICollection>.CopyTo(System.Collections.Generic.KeyValuePair[] array, int index) { } @@ -122,10 +123,22 @@ void System.Collections.IDictionary.Remove(object key) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public System.Collections.Generic.KeyValuePair[] ToArray() { throw null; } public bool TryAdd(TKey key, TValue value) { throw null; } + public bool TryGetAlternateLookup(out AlternateLookup lookup) where TAlternateKey : notnull, allows ref struct { throw null; } public bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } public bool TryRemove(System.Collections.Generic.KeyValuePair item) { throw null; } public bool TryRemove(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { throw null; } + public readonly struct AlternateLookup where TAlternateKey : notnull, allows ref struct + { + public System.Collections.Concurrent.ConcurrentDictionary Dictionary { get { throw null; } } + public TValue this[TAlternateKey key] { get { throw null; } set { } } + public bool ContainsKey(TAlternateKey key) { throw null; } + public bool TryAdd(TAlternateKey key, TValue value) { throw null; } + public bool TryGetValue(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public bool TryGetValue(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TKey actualKey, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public bool TryRemove(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public bool TryRemove(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TKey actualKey, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + } } public partial class ConcurrentStack : System.Collections.Concurrent.IProducerConsumerCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.ICollection, System.Collections.IEnumerable { diff --git a/src/libraries/System.Collections.Concurrent/src/Resources/Strings.resx b/src/libraries/System.Collections.Concurrent/src/Resources/Strings.resx index 68f6daa1cd2f8..b15258417a5b2 100644 --- a/src/libraries/System.Collections.Concurrent/src/Resources/Strings.resx +++ b/src/libraries/System.Collections.Concurrent/src/Resources/Strings.resx @@ -153,4 +153,7 @@ Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + The collection's comparer does not support the requested operation. + diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs index e6ae682a9c853..7f63ffb0f8b20 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs @@ -223,6 +223,50 @@ internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockA _budget = buckets.Length / locks.Length; } + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The alternate type of a key for performing lookups. + /// The created lookup instance. + /// This instance's comparer is not compatible with . + /// + /// This instance must be using a comparer that implements with + /// and . If it doesn't, an exception will be thrown. + /// + public AlternateLookup GetAlternateLookup() where TAlternateKey : notnull, allows ref struct + { + if (!IsCompatibleKey(_tables)) + { + ThrowHelper.ThrowIncompatibleComparer(); + } + + return new AlternateLookup(this); + } + + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The alternate type of a key for performing lookups. + /// The created lookup instance when the method returns true, or a default instance that should not be used if the method returns false. + /// true if a lookup could be created; otherwise, false. + /// + /// This instance must be using a comparer that implements with + /// and . If it doesn't, the method will return false. + /// + public bool TryGetAlternateLookup(out AlternateLookup lookup) where TAlternateKey : notnull, allows ref struct + { + if (IsCompatibleKey(_tables)) + { + lookup = new AlternateLookup(this); + return true; + } + + lookup = default; + return false; + } + /// Computes an initial capacity to use based on an initial seed collection. /// The collection with which to initially populate this dictionary. /// The capacity to use. @@ -1201,6 +1245,7 @@ public TValue GetOrAdd(TKey key, Func valueFactory) /// key is already in the dictionary, or the new value for the key as returned by valueFactory /// if the key was not in the dictionary. public TValue GetOrAdd(TKey key, Func valueFactory, TArg factoryArgument) + where TArg : allows ref struct { if (key is null) { @@ -1279,6 +1324,7 @@ public TValue GetOrAdd(TKey key, TValue value) /// absent) or the result of updateValueFactory (if the key was present). public TValue AddOrUpdate( TKey key, Func addValueFactory, Func updateValueFactory, TArg factoryArgument) + where TArg : allows ref struct { if (key is null) { @@ -2288,6 +2334,347 @@ private sealed class DictionaryEnumerator : IDictionaryEnumerator public void Reset() => _enumerator.Reset(); } + + /// Checks whether the dictionary has a comparer compatible with . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsCompatibleKey(ConcurrentDictionary.Tables tables) + where TAlternateKey : notnull, allows ref struct + { + Debug.Assert(tables is not null); + return tables._comparer is IAlternateEqualityComparer; + } + + /// Gets the dictionary's alternate comparer. The dictionary must have already been verified as compatible. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IAlternateEqualityComparer GetAlternateComparer(ConcurrentDictionary.Tables tables) + where TAlternateKey : notnull, allows ref struct + { + Debug.Assert(IsCompatibleKey(tables)); + return Unsafe.As>(tables._comparer!); + } + + /// + /// Provides a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The alternate type of a key for performing lookups. + public readonly struct AlternateLookup where TAlternateKey : notnull, allows ref struct + { + /// Initialize the instance. The dictionary must have already been verified to have a compatible comparer. + internal AlternateLookup(ConcurrentDictionary dictionary) + { + Debug.Assert(dictionary is not null); + Debug.Assert(IsCompatibleKey(dictionary._tables)); + Dictionary = dictionary; + } + + /// Gets the against which this instance performs operations. + public ConcurrentDictionary Dictionary { get; } + + /// Gets or sets the value associated with the specified alternate key. + /// The alternate key of the value to get or set. + /// + /// The value associated with the specified alternate key. If the specified alternate key is not found, a get operation throws + /// a , and a set operation creates a new element with the specified key. + /// + /// is . + /// The property is retrieved and alternate key does not exist in the collection. + public TValue this[TAlternateKey key] + { + get => TryGetValue(key, out TValue? value) ? value : throw new KeyNotFoundException(); + set => TryAdd(key, value, updateIfExists: true, out _); + } + + /// Determines whether the contains the specified alternate key. + /// The alternate key to check. + /// if the key is in the dictionary; otherwise, . + /// is . + public bool ContainsKey(TAlternateKey key) => + TryGetValue(key, out _); + + /// Attempts to add the specified key and value to the dictionary. + /// The alternate key of the element to add. + /// The value of the element to add. + /// true if the key/value pair was added to the dictionary successfully; otherwise, false. + /// is . + public bool TryAdd(TAlternateKey key, TValue value) => + TryAdd(key, value, updateIfExists: false, out _); + + /// Attempts to add the specified key and value to the dictionary. + /// The alternate key of the element to add. + /// The value of the element to add. + /// true to overwrite the value of an existing key; false to throw for an existing key. + /// The value associated with the key when the operation completes. + private bool TryAdd(TAlternateKey key, TValue value, bool updateIfExists, out TValue resultingValue) + { + if (key is null) + { + ThrowHelper.ThrowKeyNullException(); + } + + Tables tables = Dictionary._tables; + IAlternateEqualityComparer comparer = GetAlternateComparer(tables); + + int hashcode = comparer.GetHashCode(key); + + while (true) + { + object[] locks = tables._locks; + ref Node? bucket = ref GetBucketAndLock(tables, hashcode, out uint lockNo); + + bool resizeDesired = false; + bool forceRehash = false; + bool lockTaken = false; + try + { + Monitor.Enter(locks[lockNo], ref lockTaken); + + // If the table just got resized, we may not be holding the right lock, and must retry. + // This should be a rare occurrence. + if (tables != Dictionary._tables) + { + tables = Dictionary._tables; + if (!ReferenceEquals(comparer, tables._comparer)) + { + comparer = GetAlternateComparer(tables); + hashcode = comparer.GetHashCode(key); + } + continue; + } + + // Try to find this key in the bucket + uint collisionCount = 0; + Node? prev = null; + for (Node? node = bucket; node is not null; node = node._next) + { + Debug.Assert((prev is null && node == bucket) || prev!._next == node); + if (hashcode == node._hashcode && comparer.Equals(key, node._key)) + { + // The key was found in the dictionary. If updates are allowed, update the value for that key. + // We need to create a new node for the update, in order to support TValue types that cannot + // be written atomically, since lock-free reads may be happening concurrently. + if (updateIfExists) + { + // Do the reference type check up front to handle many cases of shared generics. + // If TValue is a value type then the field's value here can be baked in. Otherwise, + // for the remaining shared generic cases the field access here would disqualify inlining, + // so the following check cannot be factored out of TryAddInternal/TryUpdateInternal. + if (!typeof(TValue).IsValueType || ConcurrentDictionaryTypeProps.IsWriteAtomic) + { + node._value = value; + } + else + { + var newNode = new Node(node._key, value, hashcode, node._next); + if (prev is null) + { + Volatile.Write(ref bucket, newNode); + } + else + { + prev._next = newNode; + } + } + resultingValue = value; + } + else + { + resultingValue = node._value; + } + return false; + } + prev = node; + if (!typeof(TKey).IsValueType) // this is only relevant to strings, and we can avoid this code for all value types + { + collisionCount++; + } + } + + TKey actualKey = comparer.Create(key); + if (actualKey is null) + { + ThrowHelper.ThrowKeyNullException(); + } + + // The key was not found in the bucket. Insert the key-value pair. + var resultNode = new Node(actualKey, value, hashcode, bucket); + Volatile.Write(ref bucket, resultNode); + checked + { + tables._countPerLock[lockNo]++; + } + + // If the number of elements guarded by this lock has exceeded the budget, resize the bucket table. + // It is also possible that GrowTable will increase the budget but won't resize the bucket table. + // That happens if the bucket table is found to be poorly utilized due to a bad hash function. + if (tables._countPerLock[lockNo] > Dictionary._budget) + { + resizeDesired = true; + } + + // We similarly want to invoke redo the tables if we're using a non-randomized comparer + // and need to upgrade to a randomized comparer due to too many collisions. + if (!typeof(TKey).IsValueType && + collisionCount > HashHelpers.HashCollisionThreshold && + comparer is NonRandomizedStringEqualityComparer) + { + forceRehash = true; + } + } + finally + { + if (lockTaken) + { + Monitor.Exit(locks[lockNo]); + } + } + + // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table. + // See comments in TryAddInternal. + if (resizeDesired | forceRehash) + { + Dictionary.GrowTable(tables, resizeDesired, forceRehash); + } + + resultingValue = value; + return true; + } + } + + /// Gets the value associated with the specified alternate key. + /// The alternate key of the value to get. + /// + /// When this method returns, contains the value associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the value parameter. + /// + /// is . + public bool TryGetValue(TAlternateKey key, [MaybeNullWhen(false)] out TValue value) => + TryGetValue(key, out _, out value); + + /// Gets the value associated with the specified alternate key. + /// The alternate key of the value to get. + /// + /// When this method returns, contains the actual key associated with the alternate key, if the key is found; + /// otherwise, the default value for the type of the key parameter. + /// + /// + /// When this method returns, contains the value associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the value parameter. + /// + /// is . + public bool TryGetValue(TAlternateKey key, [MaybeNullWhen(false)] out TKey actualKey, [MaybeNullWhen(false)] out TValue value) + { + if (key is null) + { + ThrowHelper.ThrowKeyNullException(); + } + + Tables tables = Dictionary._tables; + IAlternateEqualityComparer comparer = GetAlternateComparer(tables); + + int hashcode = comparer.GetHashCode(key); + for (Node? n = GetBucket(tables, hashcode); n is not null; n = n._next) + { + if (hashcode == n._hashcode && comparer.Equals(key, n._key)) + { + actualKey = n._key; + value = n._value; + return true; + } + } + + actualKey = default; + value = default; + return false; + } + + /// + /// Removes the value with the specified alternate key from the , + /// and copies the element to the value parameter. + /// + /// The alternate key of the element to remove. + /// The removed element. + /// true if the element is successfully found and removed; otherwise, false. + /// is . + public bool TryRemove(TAlternateKey key, [MaybeNullWhen(false)] out TValue value) => + TryRemove(key, out _, out value); + + /// + /// Removes the value with the specified alternate key from the , + /// and copies the associated key and element to the value parameter. + /// + /// The alternate key of the element to remove. + /// The removed key. + /// The removed element. + /// true if the element is successfully found and removed; otherwise, false. + /// is . + public bool TryRemove(TAlternateKey key, [MaybeNullWhen(false)] out TKey actualKey, [MaybeNullWhen(false)] out TValue value) + { + if (key is null) + { + ThrowHelper.ThrowKeyNullException(); + } + + Tables tables = Dictionary._tables; + IAlternateEqualityComparer comparer = GetAlternateComparer(tables); + int hashcode = comparer.GetHashCode(key); + + while (true) + { + object[] locks = tables._locks; + ref Node? bucket = ref GetBucketAndLock(tables, hashcode, out uint lockNo); + + // Do a hot read on number of items stored in the bucket. If it's empty, we can avoid + // taking the lock and fail fast. + if (tables._countPerLock[lockNo] != 0) + { + lock (locks[lockNo]) + { + // If the table just got resized, we may not be holding the right lock, and must retry. + // This should be a rare occurrence. + if (tables != Dictionary._tables) + { + tables = Dictionary._tables; + if (!ReferenceEquals(comparer, tables._comparer)) + { + comparer = GetAlternateComparer(tables); + hashcode = comparer.GetHashCode(key); + } + continue; + } + + Node? prev = null; + for (Node? curr = bucket; curr is not null; curr = curr._next) + { + Debug.Assert((prev is null && curr == bucket) || prev!._next == curr); + + if (hashcode == curr._hashcode && comparer.Equals(key, curr._key)) + { + if (prev is null) + { + Volatile.Write(ref bucket, curr._next); + } + else + { + prev._next = curr._next; + } + + actualKey = curr._key; + value = curr._value; + tables._countPerLock[lockNo]--; + return true; + } + prev = curr; + } + } + } + + actualKey = default; + value = default; + return false; + } + } + } } internal static class ConcurrentDictionaryTypeProps diff --git a/src/libraries/System.Collections.Concurrent/src/System/ThrowHelper.cs b/src/libraries/System.Collections.Concurrent/src/System/ThrowHelper.cs index d76c09080b28b..bb8a35bac4e41 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/ThrowHelper.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/ThrowHelper.cs @@ -21,5 +21,8 @@ internal static class ThrowHelper [DoesNotReturn] internal static void ThrowOutOfMemoryException() => throw new OutOfMemoryException(); + + [DoesNotReturn] + internal static void ThrowIncompatibleComparer() => throw new InvalidOperationException(SR.InvalidOperation_IncompatibleComparer); } } diff --git a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs index 454b6916c2b11..216ee55b41779 100644 --- a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs +++ b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs @@ -1088,7 +1088,198 @@ public void ConcurrentWriteRead_NoTornValues() })); } + // TODO: Revise this test when EqualityComparer.Default implements IAlternateEqualityComparer, string> + [Fact] + public void GetAlternateLookup_FailsForDefaultComparer() + { + Assert.False(new ConcurrentDictionary().TryGetAlternateLookup>(out _)); + } + + [Fact] + public void GetAlternateLookup_FailsWhenIncompatible() + { + var dictionary = new ConcurrentDictionary(StringComparer.Ordinal); + + dictionary.GetAlternateLookup>(); + Assert.True(dictionary.TryGetAlternateLookup>(out _)); + + Assert.Throws(() => dictionary.GetAlternateLookup>()); + Assert.Throws(() => dictionary.GetAlternateLookup()); + Assert.Throws(() => dictionary.GetAlternateLookup()); + + Assert.False(dictionary.TryGetAlternateLookup>(out _)); + Assert.False(dictionary.TryGetAlternateLookup(out _)); + Assert.False(dictionary.TryGetAlternateLookup(out _)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void GetAlternateLookup_OperationsMatchUnderlyingDictionary(int mode) + { + // Test with a variety of comparers to ensure that the alternate lookup is consistent with the underlying dictionary + ConcurrentDictionary dictionary = new(mode switch + { + 0 => StringComparer.Ordinal, + 1 => StringComparer.OrdinalIgnoreCase, + 2 => StringComparer.InvariantCulture, + 3 => StringComparer.InvariantCultureIgnoreCase, + 4 => StringComparer.CurrentCulture, + 5 => StringComparer.CurrentCultureIgnoreCase, + _ => throw new ArgumentOutOfRangeException(nameof(mode)) + }); + ConcurrentDictionary.AlternateLookup> lookup = dictionary.GetAlternateLookup>(); + Assert.Same(dictionary, lookup.Dictionary); + Assert.Same(lookup.Dictionary, lookup.Dictionary); + + string actualKey; + int value; + + // Add to the dictionary and validate that the lookup reflects the changes + dictionary["123"] = 123; + Assert.True(lookup.ContainsKey("123".AsSpan())); + Assert.True(lookup.TryGetValue("123".AsSpan(), out value)); + Assert.Equal(123, value); + Assert.Equal(123, lookup["123".AsSpan()]); + Assert.False(lookup.TryAdd("123".AsSpan(), 321)); + Assert.True(lookup.TryRemove("123".AsSpan(), out value)); + Assert.Equal(123, value); + Assert.False(dictionary.ContainsKey("123")); + Assert.Throws(() => lookup["123".AsSpan()]); + + // Add via the lookup and validate that the dictionary reflects the changes + Assert.True(lookup.TryAdd("123".AsSpan(), 123)); + Assert.True(dictionary.ContainsKey("123")); + lookup.TryGetValue("123".AsSpan(), out value); + Assert.Equal(123, value); + Assert.False(lookup.TryRemove("321".AsSpan(), out actualKey, out value)); + Assert.Null(actualKey); + Assert.Equal(0, value); + Assert.True(lookup.TryRemove("123".AsSpan(), out actualKey, out value)); + Assert.Equal("123", actualKey); + Assert.Equal(123, value); + + // Ensure that case-sensitivity of the comparer is respected + lookup["a".AsSpan()] = 42; + if (dictionary.Comparer.Equals(StringComparer.Ordinal) || + dictionary.Comparer.Equals(StringComparer.InvariantCulture) || + dictionary.Comparer.Equals(StringComparer.CurrentCulture)) + { + Assert.True(lookup.TryGetValue("a".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(42, value); + Assert.True(lookup.TryAdd("A".AsSpan(), 42)); + Assert.True(lookup.TryRemove("a".AsSpan(), out value)); + Assert.Equal(42, value); + Assert.False(lookup.TryRemove("a".AsSpan(), out value)); + Assert.Equal(0, value); + Assert.True(lookup.TryRemove("A".AsSpan(), out value)); + Assert.Equal(42, value); + } + else + { + Assert.True(lookup.TryGetValue("A".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(42, value); + Assert.False(lookup.TryAdd("A".AsSpan(), 42)); + Assert.True(lookup.TryRemove("A".AsSpan(), out value)); + Assert.Equal(42, value); + Assert.False(lookup.TryRemove("a".AsSpan(), out value)); + Assert.Equal(0, value); + Assert.False(lookup.TryRemove("A".AsSpan(), out value)); + Assert.Equal(0, value); + } + + // Validate overwrites + lookup["a".AsSpan()] = 42; + Assert.Equal(42, dictionary["a"]); + lookup["a".AsSpan()] = 43; + Assert.True(lookup.TryRemove("a".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(43, value); + + // Test adding multiple entries via the lookup + for (int i = 0; i < 10; i++) + { + Assert.Equal(i, dictionary.Count); + Assert.True(lookup.TryAdd(i.ToString().AsSpan(), i)); + Assert.False(lookup.TryAdd(i.ToString().AsSpan(), i)); + } + + Assert.Equal(10, dictionary.Count); + + // Test that the lookup and the dictionary agree on what's in and not in + for (int i = -1; i <= 10; i++) + { + Assert.Equal(dictionary.TryGetValue(i.ToString(), out int dv), lookup.TryGetValue(i.ToString().AsSpan(), out int lv)); + Assert.Equal(dv, lv); + } + + // Test removing multiple entries via the lookup + for (int i = 9; i >= 0; i--) + { + Assert.True(lookup.TryRemove(i.ToString().AsSpan(), out actualKey, out value)); + Assert.Equal(i.ToString(), actualKey); + Assert.Equal(i, value); + Assert.False(lookup.TryRemove(i.ToString().AsSpan(), out actualKey, out value)); + Assert.Null(actualKey); + Assert.Equal(0, value); + Assert.Equal(i, dictionary.Count); + } + } + + [Fact] + public void Dictionary_NotCorruptedByThrowingComparer() + { + ConcurrentDictionary dict = new(new CreateThrowsComparer()); + + Assert.Equal(0, dict.Count); + + Assert.Throws(() => dict.GetAlternateLookup>().TryAdd("123".AsSpan(), "123")); + Assert.Equal(0, dict.Count); + + Assert.True(dict.TryAdd("123", "123")); + Assert.Equal(1, dict.Count); + } + + [Fact] + public void Dictionary_NotCorruptedByNullReturningComparer() + { + ConcurrentDictionary dict = new(new NullReturningComparer()); + + Assert.Equal(0, dict.Count); + + Assert.ThrowsAny(() => dict.GetAlternateLookup>().TryAdd("123".AsSpan(), "123")); + Assert.Equal(0, dict.Count); + + Assert.True(dict.TryAdd("123", "123")); + Assert.Equal(1, dict.Count); + } + #region Helper Classes and Methods + private sealed class CreateThrowsComparer : IEqualityComparer, IAlternateEqualityComparer, string> + { + public bool Equals(string? x, string? y) => EqualityComparer.Default.Equals(x, y); + public int GetHashCode(string obj) => EqualityComparer.Default.GetHashCode(obj); + + public bool Equals(ReadOnlySpan span, string target) => span.SequenceEqual(target); + public int GetHashCode(ReadOnlySpan span) => string.GetHashCode(span); + public string Create(ReadOnlySpan span) => throw new FormatException(); + } + + private sealed class NullReturningComparer : IEqualityComparer, IAlternateEqualityComparer, string> + { + public bool Equals(string? x, string? y) => EqualityComparer.Default.Equals(x, y); + public int GetHashCode(string obj) => EqualityComparer.Default.GetHashCode(obj); + + public bool Equals(ReadOnlySpan span, string target) => span.SequenceEqual(target); + public int GetHashCode(ReadOnlySpan span) => string.GetHashCode(span); + public string Create(ReadOnlySpan span) => null!; + } private class ThreadData { diff --git a/src/libraries/System.Collections.Immutable/README.md b/src/libraries/System.Collections.Immutable/README.md index 8d002fc6bb94d..a08107edf0a5c 100644 --- a/src/libraries/System.Collections.Immutable/README.md +++ b/src/libraries/System.Collections.Immutable/README.md @@ -1,7 +1,7 @@ # System.Collections.Immutable This is the assembly that surfaces immutable collections such as [`ImmutableArray`](https://learn.microsoft.com/dotnet/api/system.collections.immutable.immutablearray-1) and [`ImmutableDictionary`](https://learn.microsoft.com/dotnet/api/system.collections.immutable.immutabledictionary-2). -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.collections.immutable. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs b/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs index bf180cfb33dbc..8e452d7e1d1e3 100644 --- a/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs +++ b/src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs @@ -200,8 +200,16 @@ public static partial class ImmutableArray public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Generic.IEnumerable items) { throw null; } public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, System.Func selector) { throw null; } public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, int start, int length, System.Func selector) { throw null; } - public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, System.Func selector, TArg arg) { throw null; } - public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, int start, int length, System.Func selector, TArg arg) { throw null; } + public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, System.Func selector, TArg arg) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif + { throw null; } + public static System.Collections.Immutable.ImmutableArray CreateRange(System.Collections.Immutable.ImmutableArray items, int start, int length, System.Func selector, TArg arg) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif + { throw null; } public static System.Collections.Immutable.ImmutableArray Create() { throw null; } public static System.Collections.Immutable.ImmutableArray Create(System.Collections.Immutable.ImmutableArray items, int start, int length) { throw null; } public static System.Collections.Immutable.ImmutableArray Create(T item) { throw null; } @@ -672,7 +680,11 @@ public static partial class ImmutableInterlocked public static void Enqueue(ref System.Collections.Immutable.ImmutableQueue location, T value) { } public static TValue GetOrAdd(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, System.Func valueFactory) where TKey : notnull { throw null; } public static TValue GetOrAdd(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, TValue value) where TKey : notnull { throw null; } - public static TValue GetOrAdd(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, System.Func valueFactory, TArg factoryArgument) where TKey : notnull { throw null; } + public static TValue GetOrAdd(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, System.Func valueFactory, TArg factoryArgument) where TKey : notnull +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif + { throw null; } public static System.Collections.Immutable.ImmutableArray InterlockedCompareExchange(ref System.Collections.Immutable.ImmutableArray location, System.Collections.Immutable.ImmutableArray value, System.Collections.Immutable.ImmutableArray comparand) { throw null; } public static System.Collections.Immutable.ImmutableArray InterlockedExchange(ref System.Collections.Immutable.ImmutableArray location, System.Collections.Immutable.ImmutableArray value) { throw null; } public static bool InterlockedInitialize(ref System.Collections.Immutable.ImmutableArray location, System.Collections.Immutable.ImmutableArray value) { throw null; } @@ -683,9 +695,17 @@ public static void Push(ref System.Collections.Immutable.ImmutableStack lo public static bool TryRemove(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) where TKey : notnull { throw null; } public static bool TryUpdate(ref System.Collections.Immutable.ImmutableDictionary location, TKey key, TValue newValue, TValue comparisonValue) where TKey : notnull { throw null; } public static bool Update(ref T location, System.Func transformer) where T : class? { throw null; } - public static bool Update(ref T location, System.Func transformer, TArg transformerArgument) where T : class? { throw null; } + public static bool Update(ref T location, System.Func transformer, TArg transformerArgument) where T : class? +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif + { throw null; } public static bool Update(ref System.Collections.Immutable.ImmutableArray location, Func, System.Collections.Immutable.ImmutableArray> transformer) { throw null; } - public static bool Update(ref System.Collections.Immutable.ImmutableArray location, Func, TArg, System.Collections.Immutable.ImmutableArray> transformer, TArg transformerArgument) { throw null; } + public static bool Update(ref System.Collections.Immutable.ImmutableArray location, Func, TArg, System.Collections.Immutable.ImmutableArray> transformer, TArg transformerArgument) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif + { throw null; } } public static partial class ImmutableList { diff --git a/src/libraries/System.Collections.Immutable/src/PACKAGE.md b/src/libraries/System.Collections.Immutable/src/PACKAGE.md index 0ca0b161aa448..78c81a5908944 100644 --- a/src/libraries/System.Collections.Immutable/src/PACKAGE.md +++ b/src/libraries/System.Collections.Immutable/src/PACKAGE.md @@ -62,8 +62,8 @@ The main types provided by this library are: -- [Collections and Data Structures](https://docs.microsoft.com/dotnet/standard/collections/) -- [API documentation](https://docs.microsoft.com/dotnet/api/system.collections.immutable) +- [Collections and Data Structures](https://learn.microsoft.com/dotnet/standard/collections/) +- [API documentation](https://learn.microsoft.com/dotnet/api/system.collections.immutable) ## Feedback & Contributing diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs index 8f244bc8305bd..15de249335b8f 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs @@ -337,6 +337,9 @@ public static ImmutableArray CreateRange(ImmutableArr /// the source array. /// public static ImmutableArray CreateRange(ImmutableArray items, Func selector, TArg arg) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif { Requires.NotNull(selector, nameof(selector)); @@ -370,6 +373,9 @@ public static ImmutableArray CreateRange(Immuta /// included in the resulting array. /// public static ImmutableArray CreateRange(ImmutableArray items, int start, int length, Func selector, TArg arg) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif { int itemsLength = items.Length; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableInterlocked.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableInterlocked.cs index 397ae8338b9b5..573d2764266f3 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableInterlocked.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableInterlocked.cs @@ -76,6 +76,9 @@ public static bool Update(ref T location, Func transformer) where T : c /// invocation of returned the existing value. /// public static bool Update(ref T location, Func transformer, TArg transformerArgument) where T : class? +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif { Requires.NotNull(transformer, nameof(transformer)); @@ -162,6 +165,9 @@ public static bool Update(ref ImmutableArray location, Func returned the existing value. /// public static bool Update(ref ImmutableArray location, Func, TArg, ImmutableArray> transformer, TArg transformerArgument) +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif { Requires.NotNull(transformer, nameof(transformer)); @@ -241,7 +247,11 @@ public static bool InterlockedInitialize(ref ImmutableArray location, Immu /// The function to execute to obtain the value to insert into the dictionary if the key is not found. /// The argument to pass to the value factory. /// The value obtained from the dictionary or if it was not present. - public static TValue GetOrAdd(ref ImmutableDictionary location, TKey key, Func valueFactory, TArg factoryArgument) where TKey : notnull + public static TValue GetOrAdd(ref ImmutableDictionary location, TKey key, Func valueFactory, TArg factoryArgument) + where TKey : notnull +#if NET9_0_OR_GREATER + where TArg : allows ref struct +#endif { Requires.NotNull(valueFactory, nameof(valueFactory)); diff --git a/src/libraries/System.Collections.NonGeneric/README.md b/src/libraries/System.Collections.NonGeneric/README.md index e7486f5491a23..4d1bd77dfb9f2 100644 --- a/src/libraries/System.Collections.NonGeneric/README.md +++ b/src/libraries/System.Collections.NonGeneric/README.md @@ -1,9 +1,9 @@ # System.Collections.NonGeneric -This is the assembly that generally surfaces most non-generic collections such as [`Stack`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.stack) and [`Queue`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.queue). +This is the assembly that generally surfaces most non-generic collections such as [`Stack`](https://learn.microsoft.com/dotnet/api/system.collections.stack) and [`Queue`](https://learn.microsoft.com/dotnet/api/system.collections.queue). -Non-generic collections that are used by lower-level parts of the framework, such as [`IList`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ilist) and [`Hashtable`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.hashtable) are surfaced by the `System.Runtime` assembly. The implementations for these collections live in [System.Private.Corelib](../System.Private.Corelib/src/System/Collections/). +Non-generic collections that are used by lower-level parts of the framework, such as [`IList`](https://learn.microsoft.com/dotnet/api/system.collections.ilist) and [`Hashtable`](https://learn.microsoft.com/dotnet/api/system.collections.hashtable) are surfaced by the `System.Runtime` assembly. The implementations for these collections live in [System.Private.Corelib](../System.Private.Corelib/src/System/Collections/). -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/collections. +Documentation can be found at https://learn.microsoft.com/dotnet/csharp/programming-guide/concepts/collections. ## Contribution Bar - [x] [We only consider fixes to maintain or improve quality](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Collections.Specialized/README.md b/src/libraries/System.Collections.Specialized/README.md index bb7944c83da59..19c6304bf1a46 100644 --- a/src/libraries/System.Collections.Specialized/README.md +++ b/src/libraries/System.Collections.Specialized/README.md @@ -1,7 +1,7 @@ # System.Collections.Specialized This is the assembly that surfaces specialized collections; for example, [`LinkedDictionary`](https://learn.microsoft.com/dotnet/api/system.collections.specialized.listdictionary), [`BitVector`](https://learn.microsoft.com/dotnet/api/system.collections.specialized.bitvector), and [`StringCollection`](https://learn.microsoft.com/dotnet/api/system.collections.specialized.stringcollection). -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.collections.specialized. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.collections.specialized. ## Contribution Bar - [x] [We only consider lower-risk or high-impact fixes to maintain or improve quality](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Collections/README.md b/src/libraries/System.Collections/README.md index 6ec616de5d628..5fcd64a0e72c2 100644 --- a/src/libraries/System.Collections/README.md +++ b/src/libraries/System.Collections/README.md @@ -3,7 +3,7 @@ This is the assembly that generally surfaces generic collections such as [`List< Generic collection interfaces that are used by lower-level parts of the framework, such as [`IList`](https://learn.microsoft.com/dotnet/api/system.collections.generic.ilist-1) and [`IAsyncEnumerable`](https://learn.microsoft.com/dotnet/api/system.collections.generic.iasyncenumerable-1) are surfaced by the `System.Runtime` assembly. The implementations for these collections live in [System.Private.Corelib](../System.Private.Corelib/src/System/Collections/Generic). -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/collections. +Documentation can be found at https://learn.microsoft.com/dotnet/csharp/programming-guide/concepts/collections. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Collections/ref/System.Collections.cs b/src/libraries/System.Collections/ref/System.Collections.cs index 6d8c4f6f27389..17420dfca8dc6 100644 --- a/src/libraries/System.Collections/ref/System.Collections.cs +++ b/src/libraries/System.Collections/ref/System.Collections.cs @@ -417,12 +417,16 @@ namespace System.Collections.Generic public static partial class CollectionExtensions { public static void AddRange(this System.Collections.Generic.List list, params System.ReadOnlySpan source) { } + public static System.Collections.Generic.Dictionary.AlternateLookup GetAlternateLookup(this System.Collections.Generic.Dictionary dictionary) where TKey : notnull where TAlternateKey : notnull, allows ref struct { throw null; } + public static System.Collections.Generic.HashSet.AlternateLookup GetAlternateLookup(this System.Collections.Generic.HashSet set) where TAlternate : allows ref struct { throw null; } public static void CopyTo(this System.Collections.Generic.List list, System.Span destination) { } public static TValue? GetValueOrDefault(this System.Collections.Generic.IReadOnlyDictionary dictionary, TKey key) { throw null; } public static TValue GetValueOrDefault(this System.Collections.Generic.IReadOnlyDictionary dictionary, TKey key, TValue defaultValue) { throw null; } public static void InsertRange(this System.Collections.Generic.List list, int index, params System.ReadOnlySpan source) { } public static bool Remove(this System.Collections.Generic.IDictionary dictionary, TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } public static bool TryAdd(this System.Collections.Generic.IDictionary dictionary, TKey key, TValue value) { throw null; } + public static bool TryGetAlternateLookup(this System.Collections.Generic.Dictionary dictionary, out System.Collections.Generic.Dictionary.AlternateLookup lookup) where TKey : notnull where TAlternateKey : notnull, allows ref struct { throw null; } + public static bool TryGetAlternateLookup(this System.Collections.Generic.HashSet set, out System.Collections.Generic.HashSet.AlternateLookup lookup) where TAlternate : allows ref struct { throw null; } public static System.Collections.ObjectModel.ReadOnlyCollection AsReadOnly(this IList list) { throw null; } public static System.Collections.ObjectModel.ReadOnlyDictionary AsReadOnly(this IDictionary dictionary) where TKey : notnull { throw null; } } @@ -492,6 +496,17 @@ public void TrimExcess() { } public void TrimExcess(int capacity) { } public bool TryAdd(TKey key, TValue value) { throw null; } public bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public readonly partial struct AlternateLookup where TAlternateKey : notnull, allows ref struct + { + public System.Collections.Generic.Dictionary Dictionary { get { throw null; } } + public TValue this[TAlternateKey key] { get { throw null; } set { } } + public bool ContainsKey(TAlternateKey key) { throw null; } + public bool TryAdd(TAlternateKey key, TValue value) { throw null; } + public bool TryGetValue(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public bool TryGetValue(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TKey actualKey, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + public bool Remove(TAlternateKey key) { throw null; } + public bool Remove(TAlternateKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TKey actualKey, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + } public partial struct Enumerator : System.Collections.Generic.IEnumerator>, System.Collections.IDictionaryEnumerator, System.Collections.IEnumerator, System.IDisposable { private object _dummy; @@ -618,6 +633,14 @@ public void TrimExcess() { } public void TrimExcess(int capacity) { } public bool TryGetValue(T equalValue, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out T actualValue) { throw null; } public void UnionWith(System.Collections.Generic.IEnumerable other) { } + public readonly partial struct AlternateLookup where TAlternate : allows ref struct + { + public System.Collections.Generic.HashSet Set { get { throw null; } } + public bool Add(TAlternate item) { throw null; } + public bool Contains(TAlternate item) { throw null; } + public bool Remove(TAlternate item) { throw null; } + public bool TryGetValue(TAlternate equalValue, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out T actualValue) { throw null; } + } public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { private T _current; diff --git a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml index f281317339d43..52276bd955eb1 100644 --- a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Collections/src/System/Collections/BitArray.cs b/src/libraries/System.Collections/src/System/Collections/BitArray.cs index 0f54b8a714c70..7de69a92cb4f4 100644 --- a/src/libraries/System.Collections/src/System/Collections/BitArray.cs +++ b/src/libraries/System.Collections/src/System/Collections/BitArray.cs @@ -623,7 +623,7 @@ public BitArray RightShift(int count) // In that case, we are shifting a uint by 32, which could be considered undefined. // The result of a shift operation is undefined ... if the right operand // is greater than or equal to the width in bits of the promoted left operand, - // https://docs.microsoft.com/en-us/cpp/c-language/bitwise-shift-operators?view=vs-2017 + // https://learn.microsoft.com/cpp/c-language/bitwise-shift-operators?view=vs-2017 // However, the compiler protects us from undefined behaviour by constraining the // right operand to between 0 and width - 1 (inclusive), i.e. right_operand = (right_operand % width). uint mask = uint.MaxValue >> (BitsPerInt32 - extraBits); diff --git a/src/libraries/System.Collections/tests/Generic/CollectionExtensionsTests.cs b/src/libraries/System.Collections/tests/Generic/CollectionExtensionsTests.cs index 213d9d6faca99..50e7253deadd5 100644 --- a/src/libraries/System.Collections/tests/Generic/CollectionExtensionsTests.cs +++ b/src/libraries/System.Collections/tests/Generic/CollectionExtensionsTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using Xunit; namespace System.Collections.Tests @@ -139,5 +140,324 @@ public void AsReadOnly_NullIDictionary_ThrowsArgumentNullException() IDictionary dictionary = null; Assert.Throws("dictionary", () => dictionary.AsReadOnly()); } + + [Fact] + public void GetAlternateLookup_ThrowsWhenNull() + { + AssertExtensions.Throws("dictionary", () => CollectionExtensions.GetAlternateLookup((Dictionary)null)); + AssertExtensions.Throws("dictionary", () => CollectionExtensions.TryGetAlternateLookup((Dictionary)null, out _)); + + AssertExtensions.Throws("set", () => CollectionExtensions.GetAlternateLookup((HashSet)null)); + AssertExtensions.Throws("set", () => CollectionExtensions.TryGetAlternateLookup((HashSet)null, out _)); + } + + // TODO https://github.com/dotnet/runtime/issues/102906: + // Revise this test when EqualityComparer.Default implements IAlternateEqualityComparer, string> + [Fact] + public void GetAlternateLookup_FailsForDefaultComparer() + { + Assert.False(new Dictionary().TryGetAlternateLookup>(out _)); + Assert.False(new HashSet().TryGetAlternateLookup>(out _)); + } + + [Fact] + public void GetAlternateLookup_FailsWhenIncompatible() + { + var dictionary = new Dictionary(StringComparer.Ordinal); + var hashSet = new HashSet(StringComparer.Ordinal); + + dictionary.GetAlternateLookup>(); + Assert.True(dictionary.TryGetAlternateLookup>(out _)); + + hashSet.GetAlternateLookup>(); + Assert.True(hashSet.TryGetAlternateLookup>(out _)); + + Assert.Throws(() => dictionary.GetAlternateLookup>()); + Assert.Throws(() => dictionary.GetAlternateLookup()); + Assert.Throws(() => dictionary.GetAlternateLookup()); + + Assert.False(dictionary.TryGetAlternateLookup>(out _)); + Assert.False(dictionary.TryGetAlternateLookup(out _)); + Assert.False(dictionary.TryGetAlternateLookup(out _)); + + Assert.Throws(() => hashSet.GetAlternateLookup>()); + Assert.Throws(() => hashSet.GetAlternateLookup()); + Assert.Throws(() => hashSet.GetAlternateLookup()); + + Assert.False(hashSet.TryGetAlternateLookup>(out _)); + Assert.False(hashSet.TryGetAlternateLookup(out _)); + Assert.False(hashSet.TryGetAlternateLookup(out _)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void Dictionary_GetAlternateLookup_OperationsMatchUnderlyingDictionary(int mode) + { + // Test with a variety of comparers to ensure that the alternate lookup is consistent with the underlying dictionary + Dictionary dictionary = new(mode switch + { + 0 => StringComparer.Ordinal, + 1 => StringComparer.OrdinalIgnoreCase, + 2 => StringComparer.InvariantCulture, + 3 => StringComparer.InvariantCultureIgnoreCase, + 4 => StringComparer.CurrentCulture, + 5 => StringComparer.CurrentCultureIgnoreCase, + _ => throw new ArgumentOutOfRangeException(nameof(mode)) + }); + Dictionary.AlternateLookup> lookup = dictionary.GetAlternateLookup>(); + Assert.Same(dictionary, lookup.Dictionary); + Assert.Same(lookup.Dictionary, lookup.Dictionary); + + string actualKey; + int value; + + // Add to the dictionary and validate that the lookup reflects the changes + dictionary["123"] = 123; + Assert.True(lookup.ContainsKey("123".AsSpan())); + Assert.True(lookup.TryGetValue("123".AsSpan(), out value)); + Assert.Equal(123, value); + Assert.Equal(123, lookup["123".AsSpan()]); + Assert.False(lookup.TryAdd("123".AsSpan(), 321)); + Assert.True(lookup.Remove("123".AsSpan())); + Assert.False(dictionary.ContainsKey("123")); + Assert.Throws(() => lookup["123".AsSpan()]); + + // Add via the lookup and validate that the dictionary reflects the changes + Assert.True(lookup.TryAdd("123".AsSpan(), 123)); + Assert.True(dictionary.ContainsKey("123")); + lookup.TryGetValue("123".AsSpan(), out value); + Assert.Equal(123, value); + Assert.False(lookup.Remove("321".AsSpan(), out actualKey, out value)); + Assert.Null(actualKey); + Assert.Equal(0, value); + Assert.True(lookup.Remove("123".AsSpan(), out actualKey, out value)); + Assert.Equal("123", actualKey); + Assert.Equal(123, value); + + // Ensure that case-sensitivity of the comparer is respected + lookup["a".AsSpan()] = 42; + if (dictionary.Comparer.Equals(StringComparer.Ordinal) || + dictionary.Comparer.Equals(StringComparer.InvariantCulture) || + dictionary.Comparer.Equals(StringComparer.CurrentCulture)) + { + Assert.True(lookup.TryGetValue("a".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(42, value); + Assert.True(lookup.TryAdd("A".AsSpan(), 42)); + Assert.True(lookup.Remove("a".AsSpan())); + Assert.False(lookup.Remove("a".AsSpan())); + Assert.True(lookup.Remove("A".AsSpan())); + } + else + { + Assert.True(lookup.TryGetValue("A".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(42, value); + Assert.False(lookup.TryAdd("A".AsSpan(), 42)); + Assert.True(lookup.Remove("A".AsSpan())); + Assert.False(lookup.Remove("a".AsSpan())); + Assert.False(lookup.Remove("A".AsSpan())); + } + + // Validate overwrites + lookup["a".AsSpan()] = 42; + Assert.Equal(42, dictionary["a"]); + lookup["a".AsSpan()] = 43; + Assert.True(lookup.Remove("a".AsSpan(), out actualKey, out value)); + Assert.Equal("a", actualKey); + Assert.Equal(43, value); + + // Test adding multiple entries via the lookup + for (int i = 0; i < 10; i++) + { + Assert.Equal(i, dictionary.Count); + Assert.True(lookup.TryAdd(i.ToString().AsSpan(), i)); + Assert.False(lookup.TryAdd(i.ToString().AsSpan(), i)); + } + + Assert.Equal(10, dictionary.Count); + + // Test that the lookup and the dictionary agree on what's in and not in + for (int i = -1; i <= 10; i++) + { + Assert.Equal(dictionary.TryGetValue(i.ToString(), out int dv), lookup.TryGetValue(i.ToString().AsSpan(), out int lv)); + Assert.Equal(dv, lv); + } + + // Test removing multiple entries via the lookup + for (int i = 9; i >= 0; i--) + { + Assert.True(lookup.Remove(i.ToString().AsSpan(), out actualKey, out value)); + Assert.Equal(i.ToString(), actualKey); + Assert.Equal(i, value); + Assert.False(lookup.Remove(i.ToString().AsSpan(), out actualKey, out value)); + Assert.Null(actualKey); + Assert.Equal(0, value); + Assert.Equal(i, dictionary.Count); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void HashSet_GetAlternateLookup_OperationsMatchUnderlyingSet(int mode) + { + // Test with a variety of comparers to ensure that the alternate lookup is consistent with the underlying set + HashSet set = new(mode switch + { + 0 => StringComparer.Ordinal, + 1 => StringComparer.OrdinalIgnoreCase, + 2 => StringComparer.InvariantCulture, + 3 => StringComparer.InvariantCultureIgnoreCase, + 4 => StringComparer.CurrentCulture, + 5 => StringComparer.CurrentCultureIgnoreCase, + _ => throw new ArgumentOutOfRangeException(nameof(mode)) + }); + HashSet.AlternateLookup> lookup = set.GetAlternateLookup>(); + Assert.Same(set, lookup.Set); + Assert.Same(lookup.Set, lookup.Set); + + // Add to the set and validate that the lookup reflects the changes + Assert.True(set.Add("123")); + Assert.True(lookup.Contains("123".AsSpan())); + Assert.False(lookup.Add("123".AsSpan())); + Assert.True(lookup.Remove("123".AsSpan())); + Assert.False(set.Contains("123")); + + // Add via the lookup and validate that the set reflects the changes + Assert.True(lookup.Add("123".AsSpan())); + Assert.True(set.Contains("123")); + lookup.TryGetValue("123".AsSpan(), out string value); + Assert.Equal("123", value); + Assert.False(lookup.Remove("321".AsSpan())); + Assert.True(lookup.Remove("123".AsSpan())); + + // Ensure that case-sensitivity of the comparer is respected + Assert.True(lookup.Add("a")); + if (set.Comparer.Equals(StringComparer.Ordinal) || + set.Comparer.Equals(StringComparer.InvariantCulture) || + set.Comparer.Equals(StringComparer.CurrentCulture)) + { + Assert.True(lookup.Add("A".AsSpan())); + Assert.True(lookup.Remove("a".AsSpan())); + Assert.False(lookup.Remove("a".AsSpan())); + Assert.True(lookup.Remove("A".AsSpan())); + } + else + { + Assert.False(lookup.Add("A".AsSpan())); + Assert.True(lookup.Remove("A".AsSpan())); + Assert.False(lookup.Remove("a".AsSpan())); + Assert.False(lookup.Remove("A".AsSpan())); + } + + // Test the behavior of null vs "" in the set and lookup + Assert.True(set.Add(null)); + Assert.True(set.Add(string.Empty)); + Assert.True(set.Contains(null)); + Assert.True(set.Contains("")); + Assert.True(lookup.Contains("".AsSpan())); + Assert.True(lookup.Remove("".AsSpan())); + Assert.Equal(1, set.Count); + Assert.False(lookup.Remove("".AsSpan())); + Assert.True(set.Remove(null)); + Assert.Equal(0, set.Count); + + // Test adding multiple entries via the lookup + for (int i = 0; i < 10; i++) + { + Assert.Equal(i, set.Count); + Assert.True(lookup.Add(i.ToString().AsSpan())); + Assert.False(lookup.Add(i.ToString().AsSpan())); + } + + Assert.Equal(10, set.Count); + + // Test that the lookup and the set agree on what's in and not in + for (int i = -1; i <= 10; i++) + { + Assert.Equal(set.TryGetValue(i.ToString(), out string dv), lookup.TryGetValue(i.ToString().AsSpan(), out string lv)); + Assert.Equal(dv, lv); + } + + // Test removing multiple entries via the lookup + for (int i = 9; i >= 0; i--) + { + Assert.True(lookup.Remove(i.ToString().AsSpan())); + Assert.False(lookup.Remove(i.ToString().AsSpan())); + Assert.Equal(i, set.Count); + } + } + + [Fact] + public void Dictionary_NotCorruptedByThrowingComparer() + { + Dictionary dict = new(new CreateThrowsComparer()); + + Assert.Equal(0, dict.Count); + + Assert.Throws(() => dict.GetAlternateLookup>().TryAdd("123".AsSpan(), "123")); + Assert.Equal(0, dict.Count); + + dict.Add("123", "123"); + Assert.Equal(1, dict.Count); + } + + [Fact] + public void Dictionary_NotCorruptedByNullReturningComparer() + { + Dictionary dict = new(new NullReturningComparer()); + + Assert.Equal(0, dict.Count); + + Assert.ThrowsAny(() => dict.GetAlternateLookup>().TryAdd("123".AsSpan(), "123")); + Assert.Equal(0, dict.Count); + + dict.Add("123", "123"); + Assert.Equal(1, dict.Count); + } + + [Fact] + public void HashSet_NotCorruptedByThrowingComparer() + { + HashSet set = new(new CreateThrowsComparer()); + + Assert.Equal(0, set.Count); + + Assert.Throws(() => set.GetAlternateLookup>().Add("123".AsSpan())); + Assert.Equal(0, set.Count); + + set.Add("123"); + Assert.Equal(1, set.Count); + } + + private sealed class CreateThrowsComparer : IEqualityComparer, IAlternateEqualityComparer, string> + { + public bool Equals(string? x, string? y) => EqualityComparer.Default.Equals(x, y); + public int GetHashCode(string obj) => EqualityComparer.Default.GetHashCode(obj); + + public bool Equals(ReadOnlySpan span, string target) => span.SequenceEqual(target); + public int GetHashCode(ReadOnlySpan span) => string.GetHashCode(span); + public string Create(ReadOnlySpan span) => throw new FormatException(); + } + + private sealed class NullReturningComparer : IEqualityComparer, IAlternateEqualityComparer, string> + { + public bool Equals(string? x, string? y) => EqualityComparer.Default.Equals(x, y); + public int GetHashCode(string obj) => EqualityComparer.Default.GetHashCode(obj); + + public bool Equals(ReadOnlySpan span, string target) => span.SequenceEqual(target); + public int GetHashCode(ReadOnlySpan span) => string.GetHashCode(span); + public string Create(ReadOnlySpan span) => null!; + } } } 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 d6809b65a0941..c9088f393e839 100644 --- a/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs +++ b/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs @@ -12,6 +12,12 @@ namespace System.Collections.Tests { public class InternalHashCodeTests { + private static Type nonRandomizedDefaultComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+DefaultComparer", throwOnError: true); + private static Type nonRandomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); + private static Type nonRandomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); + private static Type randomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); + private static Type randomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); + /// /// Given a byte array, copies it to the string, without messing with any encoding. This issue was hit on a x64 machine /// @@ -45,16 +51,11 @@ public static void OutOfBoundsRegression() [Fact] public static void ComparerImplementations_Dictionary_WithWellKnownStringComparers() { - Type nonRandomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); - Type nonRandomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); - Type randomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); - Type randomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); - // null comparer RunDictionaryTest( equalityComparer: null, - expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedOrdinalComparerType, + expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedDefaultComparerType, expectedPublicComparerBeforeCollisionThreshold: EqualityComparer.Default, expectedInternalComparerTypeAfterCollisionThreshold: randomizedOrdinalComparerType); @@ -62,7 +63,7 @@ public static void ComparerImplementations_Dictionary_WithWellKnownStringCompare RunDictionaryTest( equalityComparer: EqualityComparer.Default, - expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedOrdinalComparerType, + expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedDefaultComparerType, expectedPublicComparerBeforeCollisionThreshold: EqualityComparer.Default, expectedInternalComparerTypeAfterCollisionThreshold: randomizedOrdinalComparerType); @@ -121,16 +122,11 @@ static void RunDictionaryTest( [Fact] public static void ComparerImplementations_HashSet_WithWellKnownStringComparers() { - Type nonRandomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); - Type nonRandomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); - Type randomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); - Type randomizedOrdinalIgnoreCaseComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.RandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer", throwOnError: true); - // null comparer RunHashSetTest( equalityComparer: null, - expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedOrdinalComparerType, + expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedDefaultComparerType, expectedPublicComparerBeforeCollisionThreshold: EqualityComparer.Default, expectedInternalComparerTypeAfterCollisionThreshold: randomizedOrdinalComparerType); @@ -138,7 +134,7 @@ public static void ComparerImplementations_HashSet_WithWellKnownStringComparers( RunHashSetTest( equalityComparer: EqualityComparer.Default, - expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedOrdinalComparerType, + expectedInternalComparerTypeBeforeCollisionThreshold: nonRandomizedDefaultComparerType, expectedPublicComparerBeforeCollisionThreshold: EqualityComparer.Default, expectedInternalComparerTypeAfterCollisionThreshold: randomizedOrdinalComparerType); diff --git a/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs b/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs index 571ecd3d520a9..30b387a486fbd 100644 --- a/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs +++ b/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs @@ -763,24 +763,24 @@ static void TestComparerSerialization(IEqualityComparer eq var bf = new BinaryFormatter(); var s = new MemoryStream(); - var dict = new HashSet(equalityComparer); + var set = new HashSet(equalityComparer); - Assert.Same(equalityComparer, dict.Comparer); + Assert.Same(equalityComparer, set.Comparer); - bf.Serialize(s, dict); + bf.Serialize(s, set); s.Position = 0; - dict = (HashSet)bf.Deserialize(s); + set = (HashSet)bf.Deserialize(s); if (internalTypeName == null) { - Assert.IsType(equalityComparer.GetType(), dict.Comparer); + Assert.IsType(equalityComparer.GetType(), set.Comparer); } else { - Assert.Equal(internalTypeName, dict.Comparer.GetType().ToString()); + Assert.Equal(internalTypeName, set.Comparer.GetType().ToString()); } - Assert.True(equalityComparer.Equals(dict.Comparer)); + Assert.True(equalityComparer.Equals(set.Comparer)); } } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/CompatibilitySuppressions.xml b/src/libraries/System.Configuration.ConfigurationManager/src/CompatibilitySuppressions.xml index 95f9902596c2f..a0910bc9ea957 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Configuration.ConfigurationManager/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0002 diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/PACKAGE.md b/src/libraries/System.Configuration.ConfigurationManager/src/PACKAGE.md index 992b766942699..01688235389ea 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/PACKAGE.md +++ b/src/libraries/System.Configuration.ConfigurationManager/src/PACKAGE.md @@ -74,10 +74,10 @@ The main types provided by this library are: -* [Configure apps by using configuration files](https://docs.microsoft.com/dotnet/framework/configure-apps/) -* [System.Configuration namespace](https://docs.microsoft.com/dotnet/api/system.configuration) -* [System.Configuration.Configuration](https://docs.microsoft.com/dotnet/api/system.configuration.configuration) -* [System.Configuration.ConfigurationManager](https://docs.microsoft.com/dotnet/api/system.configuration.configurationmanager) +* [Configure apps by using configuration files](https://learn.microsoft.com/dotnet/framework/configure-apps/) +* [System.Configuration namespace](https://learn.microsoft.com/dotnet/api/system.configuration) +* [System.Configuration.Configuration](https://learn.microsoft.com/dotnet/api/system.configuration.configuration) +* [System.Configuration.ConfigurationManager](https://learn.microsoft.com/dotnet/api/system.configuration.configurationmanager) ## Feedback & Contributing diff --git a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs index f51fed48b0263..0e930653ecdb7 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @@ -1227,7 +1227,7 @@ private static unsafe int WriteFileNative(IntPtr hFile, ReadOnlySpan bytes // If the code page could be Unicode, we should use ReadConsole instead, e.g. // Note that WriteConsoleW has a max limit on num of chars to write (64K) - // [https://docs.microsoft.com/en-us/windows/console/writeconsole] + // [https://learn.microsoft.com/windows/console/writeconsole] // However, we do not need to worry about that because the StreamWriter in Console has // a much shorter buffer size anyway. int charsWritten; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionOptions.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionOptions.cs index 42082231ff87f..26870bfdb23f1 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionOptions.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionOptions.cs @@ -17,13 +17,13 @@ internal sealed partial class DbConnectionOptions // differences between OleDb and Odbc // ODBC: - // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqldriverconnect-function + // https://learn.microsoft.com/sql/odbc/reference/syntax/sqldriverconnect-function // do not support == -> = in keywords // first key-value pair wins // quote values using \{ and \}, only driver= and pwd= appear to generically allow quoting // do not strip quotes from value, or add quotes except for driver keyword // OLEDB: - // https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-string-syntax#oledb-connection-string-syntax + // https://learn.microsoft.com/dotnet/framework/data/adonet/connection-string-syntax#oledb-connection-string-syntax // support == -> = in keywords // last key-value pair wins // quote values using \" or \' diff --git a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs index 428393d70ea17..dedde6f7efbd6 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataTable.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataTable.cs @@ -1357,7 +1357,7 @@ internal IFormatProvider FormatProvider get { // used for Formating/Parsing - // https://docs.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.isneutralculture + // https://learn.microsoft.com/dotnet/api/system.globalization.cultureinfo.isneutralculture if (null == _formatProvider) { CultureInfo culture = Locale; diff --git a/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/DbConnectionOptions.cs b/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/DbConnectionOptions.cs index 2a75940d3f25d..331969fa50a21 100644 --- a/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/DbConnectionOptions.cs +++ b/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/DbConnectionOptions.cs @@ -20,13 +20,13 @@ internal partial class DbConnectionOptions // differences between OleDb and Odbc // ODBC: - // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqldriverconnect-function + // https://learn.microsoft.com/sql/odbc/reference/syntax/sqldriverconnect-function // do not support == -> = in keywords // first key-value pair wins // quote values using \{ and \}, only driver= and pwd= appear to generically allow quoting // do not strip quotes from value, or add quotes except for driver keyword // OLEDB: - // https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-string-syntax#oledb-connection-string-syntax + // https://learn.microsoft.com/dotnet/framework/data/adonet/connection-string-syntax#oledb-connection-string-syntax // support == -> = in keywords // last key-value pair wins // quote values using \" or \' diff --git a/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml b/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml index 3767ec738508f..41c22dab4f9b4 100644 --- a/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs index 1e428ff996647..9981935676117 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs @@ -101,7 +101,7 @@ internal ODBC32.SQLRETURN BeginTransaction(ref IsolationLevel isolevel) break; case IsolationLevel.Snapshot: sql_iso = ODBC32.SQL_TRANSACTION.SNAPSHOT; - // VSDD 414121: Snapshot isolation level must be set through SQL_COPT_SS_TXN_ISOLATION (https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-api/sqlsetconnectattr#sqlcoptsstxnisolation) + // VSDD 414121: Snapshot isolation level must be set through SQL_COPT_SS_TXN_ISOLATION (https://learn.microsoft.com/sql/relational-databases/native-client-odbc-api/sqlsetconnectattr#sqlcoptsstxnisolation) isolationAttribute = ODBC32.SQL_ATTR.SQL_COPT_SS_TXN_ISOLATION; break; case IsolationLevel.Chaos: diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs index 0e6be81299dba..448c882aba232 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs @@ -200,7 +200,7 @@ internal ODBC32.SQLRETURN GetDiagnosticField(out string sqlState) internal ODBC32.SQLRETURN GetDiagnosticRecord(short record, out string sqlState, StringBuilder messageBuilder, out int nativeError, out short cchActual) { // SQLGetDiagRecW expects a buffer large enough to hold a five-character state code plus a null-terminator - // See https://docs.microsoft.com/sql/odbc/reference/syntax/sqlgetdiagrec-function + // See https://learn.microsoft.com/sql/odbc/reference/syntax/sqlgetdiagrec-function char[] buffer = new char[6]; char[] message = new char[1024]; ODBC32.SQLRETURN retcode = Interop.Odbc.SQLGetDiagRecW(HandleType, this, record, buffer, out nativeError, message, checked((short)message.Length), out cchActual); diff --git a/src/libraries/System.Data.OleDb/src/CompatibilitySuppressions.xml b/src/libraries/System.Data.OleDb/src/CompatibilitySuppressions.xml index a07afbdfcfcef..3e011bfd7295e 100644 --- a/src/libraries/System.Data.OleDb/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Data.OleDb/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs b/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs index d5d84927a0918..8cc99bc095026 100644 --- a/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs +++ b/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs @@ -54,7 +54,7 @@ protected override void ReleaseObjects(IEnumerable objects) throw new NotImplementedException(); } - // Doc and type layout: https://docs.microsoft.com/windows/win32/api/oaidl/nn-oaidl-ierrorinfo + // Doc and type layout: https://learn.microsoft.com/windows/win32/api/oaidl/nn-oaidl-ierrorinfo private sealed class ErrorInfoWrapper : UnsafeNativeMethods.IErrorInfo, IDisposable { private readonly IntPtr _wrappedInstance; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 0e24044c733a7..5fcfa8f27ae44 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -50,6 +50,7 @@ public string? Id public string? TraceStateString { get { throw null; } set { } } public System.Diagnostics.Activity AddBaggage(string key, string? value) { throw null; } public System.Diagnostics.Activity AddEvent(System.Diagnostics.ActivityEvent e) { throw null; } + public System.Diagnostics.Activity AddException(System.Exception exception, in System.Diagnostics.TagList tags = default, System.DateTimeOffset timestamp = default) { throw null; } public System.Diagnostics.Activity AddLink(System.Diagnostics.ActivityLink link) { throw null; } public System.Diagnostics.Activity AddTag(string key, string? value) { throw null; } public System.Diagnostics.Activity AddTag(string key, object? value) { throw null; } @@ -150,9 +151,13 @@ public void CopyTo(System.Span destination) { } } public sealed class ActivitySource : IDisposable { + public ActivitySource(string name) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public ActivitySource(string name, string? version = "") { throw null; } + public ActivitySource(string name, string? version = "", System.Collections.Generic.IEnumerable>? tags = default) { throw null; } public string Name { get { throw null; } } public string? Version { get { throw null; } } + public System.Collections.Generic.IEnumerable>? Tags { get { throw null; } } public bool HasListeners() { throw null; } public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind) { throw null; } public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind, System.Diagnostics.ActivityContext parentContext, System.Collections.Generic.IEnumerable>? tags = null, System.Collections.Generic.IEnumerable? links = null, System.Diagnostics.ActivityIdFormat idFormat = System.Diagnostics.ActivityIdFormat.Unknown) { throw null; } @@ -272,11 +277,13 @@ public readonly struct ActivityCreationOptions public string? TraceState { get { throw null; } init { throw null; } } } public delegate System.Diagnostics.ActivitySamplingResult SampleActivity(ref System.Diagnostics.ActivityCreationOptions options); + public delegate void ExceptionRecorder(System.Diagnostics.Activity activity, System.Exception exception, ref System.Diagnostics.TagList tags); public sealed class ActivityListener : IDisposable { public ActivityListener() { throw null; } public System.Action? ActivityStarted { get { throw null; } set { throw null; } } public System.Action? ActivityStopped { get { throw null; } set { throw null; } } + public System.Diagnostics.ExceptionRecorder? ExceptionRecorder { get { throw null; } set { throw null; } } public System.Func? ShouldListenTo { get { throw null; } set { throw null; } } public System.Diagnostics.SampleActivity? SampleUsingParentId { get { throw null; } set { throw null; } } public System.Diagnostics.SampleActivity? Sample { get { throw null; } set { throw null; } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md b/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md index 54c436ae89164..d0c81bfcd9ae8 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md @@ -1,6 +1,6 @@ # Activity User Guide -**This doc is being obsoleted by: https://docs.microsoft.com/dotnet/core/diagnostics/distributed-tracing +**This doc is being obsoleted by: https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing Future doc changes should be done in the official docs, not here. There is still some information here that is not present in the official docs (yet) so I am preserving it as-is.** diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index bf2fb0bb0430a..baae3c2aa0e12 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -517,6 +517,75 @@ public Activity AddEvent(ActivityEvent e) return this; } + /// + /// Add an object containing the exception information to the list. + /// + /// The exception to add to the attached events list. + /// The tags to add to the exception event. + /// The timestamp to add to the exception event. + /// for convenient chaining. + /// + /// - The name of the event will be "exception", and it will include the tags "exception.message", "exception.stacktrace", and "exception.type", + /// in addition to the tags provided in the parameter. + /// - Any registered with the callback will be notified about this exception addition + /// before the object is added to the list. + /// - Any registered with the callback that adds "exception.message", "exception.stacktrace", or "exception.type" tags + /// will not have these tags overwritten, except by any subsequent that explicitly overwrites them. + /// + public Activity AddException(Exception exception, in TagList tags = default, DateTimeOffset timestamp = default) + { + if (exception == null) + { + throw new ArgumentNullException(nameof(exception)); + } + + TagList exceptionTags = tags; + + Source.NotifyActivityAddException(this, exception, ref exceptionTags); + + const string ExceptionEventName = "exception"; + const string ExceptionMessageTag = "exception.message"; + const string ExceptionStackTraceTag = "exception.stacktrace"; + const string ExceptionTypeTag = "exception.type"; + + bool hasMessage = false; + bool hasStackTrace = false; + bool hasType = false; + + for (int i = 0; i < exceptionTags.Count; i++) + { + if (exceptionTags[i].Key == ExceptionMessageTag) + { + hasMessage = true; + } + else if (exceptionTags[i].Key == ExceptionStackTraceTag) + { + hasStackTrace = true; + } + else if (exceptionTags[i].Key == ExceptionTypeTag) + { + hasType = true; + } + } + + if (!hasMessage) + { + exceptionTags.Add(new KeyValuePair(ExceptionMessageTag, exception.Message)); + } + + if (!hasStackTrace) + { + exceptionTags.Add(new KeyValuePair(ExceptionStackTraceTag, exception.ToString())); + } + + if (!hasType) + { + exceptionTags.Add(new KeyValuePair(ExceptionTypeTag, exception.GetType().ToString())); + } + + return AddEvent(new ActivityEvent(ExceptionEventName, timestamp, ref exceptionTags)); + } + /// /// Add an to the list. /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityEvent.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityEvent.cs index 7ea538afd901f..279d89d8f2950 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityEvent.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityEvent.cs @@ -28,12 +28,16 @@ public ActivityEvent(string name) : this(name, DateTimeOffset.UtcNow, tags: null /// Event name. /// Event timestamp. Timestamp MUST only be used for the events that happened in the past, not at the moment of this call. /// Event Tags. - public ActivityEvent(string name, DateTimeOffset timestamp = default, ActivityTagsCollection? tags = null) + public ActivityEvent(string name, DateTimeOffset timestamp = default, ActivityTagsCollection? tags = null) : this(name, timestamp, tags, tags is null ? 0 : tags.Count) { } + + internal ActivityEvent(string name, DateTimeOffset timestamp, ref TagList tags) : this(name, timestamp, tags, tags.Count) { } + + private ActivityEvent(string name, DateTimeOffset timestamp, IEnumerable>? tags, int tagsCount) { Name = name ?? string.Empty; Timestamp = timestamp != default ? timestamp : DateTimeOffset.UtcNow; - _tags = tags?.Count > 0 ? new Activity.TagsLinkedList(tags) : null; + _tags = tagsCount > 0 ? new Activity.TagsLinkedList(tags!) : null; } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityListener.cs index d9fdaee7f2b58..1ebf2699d18fa 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityListener.cs @@ -10,6 +10,11 @@ namespace System.Diagnostics /// public delegate ActivitySamplingResult SampleActivity(ref ActivityCreationOptions options); + /// + /// Define the callback to be used in to receive notifications when exceptions are added to the . + /// + public delegate void ExceptionRecorder(Activity activity, Exception exception, ref TagList tags); + /// /// ActivityListener allows listening to the start and stop Activity events and give the opportunity to decide creating the Activity for sampling scenarios. /// @@ -32,6 +37,11 @@ public ActivityListener() /// public Action? ActivityStopped { get; set; } + /// + /// Set or get the callback used to listen to events when exceptions are added. + /// + public ExceptionRecorder? ExceptionRecorder { get; set; } + /// /// Set or get the callback used to decide if want to listen to objects events which created using object. /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index e826880d89a94..ebaddab4ec80c 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading; @@ -13,16 +14,40 @@ public sealed class ActivitySource : IDisposable private static readonly SynchronizedList s_allListeners = new SynchronizedList(); private SynchronizedList? _listeners; + /// + /// Construct an ActivitySource object with the input name + /// + /// The name of the ActivitySource object + public ActivitySource(string name) : this(name, version: "", tags: null) {} + + /// + /// Construct an ActivitySource object with the input name + /// + /// The name of the ActivitySource object + /// The version of the component publishing the tracing info. + [EditorBrowsable(EditorBrowsableState.Never)] + public ActivitySource(string name, string? version = "") : this(name, version, tags: null) {} + /// /// Construct an ActivitySource object with the input name /// /// The name of the ActivitySource object /// The version of the component publishing the tracing info. - public ActivitySource(string name, string? version = "") + /// The optional ActivitySource tags. + public ActivitySource(string name, string? version = "", IEnumerable>? tags = default) { Name = name ?? throw new ArgumentNullException(nameof(name)); Version = version; + // Sorting the tags to make sure the tags are always in the same order. + // Sorting can help in comparing the tags used for any scenario. + if (tags is not null) + { + var tagList = new List>(tags); + tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal)); + Tags = tagList.AsReadOnly(); + } + s_activeSources.Add(this); if (s_allListeners.Count > 0) @@ -54,6 +79,11 @@ public ActivitySource(string name, string? version = "") /// public string? Version { get; } + /// + /// Returns the tags associated with the ActivitySource. + /// + public IEnumerable>? Tags { get; } + /// /// Check if there is any listeners for this ActivitySource. /// This property can be helpful to tell if there is no listener, then no need to create Activity object @@ -363,6 +393,18 @@ internal void NotifyActivityStop(Activity activity) listeners.EnumWithAction((listener, obj) => listener.ActivityStopped?.Invoke((Activity)obj), activity); } } + + internal void NotifyActivityAddException(Activity activity, Exception exception, ref TagList tags) + { + Debug.Assert(activity != null); + + // _listeners can get assigned to null in Dispose. + SynchronizedList? listeners = _listeners; + if (listeners != null && listeners.Count > 0) + { + listeners.EnumWithExceptionNotification(activity, exception, ref tags); + } + } } // SynchronizedList is a helper collection which ensure thread safety on the collection @@ -468,5 +510,36 @@ public void EnumWithAction(Action action, object arg) } } + public void EnumWithExceptionNotification(Activity activity, Exception exception, ref TagList tags) + { + if (typeof(T) != typeof(ActivityListener)) + { + return; + } + + uint version = _version; + int index = 0; + + while (index < _list.Count) + { + T item; + lock (_list) + { + if (version != _version) + { + version = _version; + index = 0; + continue; + } + + item = _list[index]; + index++; + } + + // Important to notify outside the lock. + // This is the whole point we are having this wrapper class. + (item as ActivityListener)!.ExceptionRecorder?.Invoke(activity, exception, ref tags); + } + } } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs index 856725680857c..b91631822585b 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs @@ -79,7 +79,7 @@ private void Initialize(string name, string? version, IEnumerable>(tags); tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal)); - Tags = tagList; + Tags = tagList.AsReadOnly(); } Scope = scope; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs index 2c82af3c0b086..017074586d583 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MeterListener.cs @@ -258,14 +258,17 @@ public void Dispose() s_allStartedListeners.Remove(this); DiagNode? current = _enabledMeasurementInstruments.First; - if (current is not null && measurementsCompleted is not null) + if (current is not null) { - callbacksArguments = new Dictionary(); + if (measurementsCompleted is not null) + { + callbacksArguments = new Dictionary(); + } do { object? state = current.Value.DisableMeasurements(this); - callbacksArguments.Add(current.Value, state); + callbacksArguments?.Add(current.Value, state); current = current.Next; } while (current is not null); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs index 98903b9df6938..86227dc2e7427 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs @@ -23,11 +23,20 @@ public void TestConstruction() Assert.Equal("Source1", as1.Name); Assert.Equal(String.Empty, as1.Version); Assert.False(as1.HasListeners()); + Assert.Null(as1.Tags); using ActivitySource as2 = new ActivitySource("Source2", "1.1.1.2"); Assert.Equal("Source2", as2.Name); Assert.Equal("1.1.1.2", as2.Version); Assert.False(as2.HasListeners()); + Assert.Null(as2.Tags); + + using ActivitySource as3 = new ActivitySource("Source3", "1.1.1.3", new TagList { { "key3", "value3" }, { "key2", "value2" }, { "key1", "value1" } }); + Assert.Equal("Source3", as3.Name); + Assert.Equal("1.1.1.3", as3.Version); + Assert.False(as3.HasListeners()); + // Ensure the tags are sorted by key. + Assert.Equal(new TagList { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }, as3.Tags); }).Dispose(); } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index 00cb26017a384..0a56bbd6772e8 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -1626,6 +1626,102 @@ public void AddLinkTest() Assert.Equal(99, tag.Value); } + [Fact] + public void AddExceptionTest() + { + using ActivitySource aSource = new ActivitySource("AddExceptionTest"); + + ActivityListener listener = new ActivityListener(); + listener.ShouldListenTo = (activitySource) => object.ReferenceEquals(activitySource, aSource); + listener.Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData; + ActivitySource.AddActivityListener(listener); + + using Activity? activity = aSource.StartActivity("Activity1"); + Assert.NotNull(activity); + Assert.Empty(activity.Events); + + const string ExceptionEventName = "exception"; + const string ExceptionMessageTag = "exception.message"; + const string ExceptionStackTraceTag = "exception.stacktrace"; + const string ExceptionTypeTag = "exception.type"; + + Exception exception = new ArgumentOutOfRangeException("Some message"); + activity.AddException(exception); + List events = activity.Events.ToList(); + Assert.Equal(1, events.Count); + Assert.Equal(ExceptionEventName, events[0].Name); + Assert.Equal(new TagList { { ExceptionMessageTag, exception.Message}, { ExceptionStackTraceTag, exception.ToString()}, { ExceptionTypeTag, exception.GetType().ToString() } }, events[0].Tags); + + try { throw new InvalidOperationException("Some other message"); } catch (Exception e) { exception = e; } + activity.AddException(exception); + events = activity.Events.ToList(); + Assert.Equal(2, events.Count); + Assert.Equal(ExceptionEventName, events[1].Name); + Assert.Equal(new TagList { { ExceptionMessageTag, exception.Message}, { ExceptionStackTraceTag, exception.ToString()}, { ExceptionTypeTag, exception.GetType().ToString() } }, events[1].Tags); + + listener.ExceptionRecorder = (Activity activity, Exception exception, ref TagList theTags) => theTags.Add("foo", "bar"); + activity.AddException(exception, new TagList { { "hello", "world" } }); + events = activity.Events.ToList(); + Assert.Equal(3, events.Count); + Assert.Equal(ExceptionEventName, events[2].Name); + Assert.Equal(new TagList + { + { "hello", "world" }, + { "foo", "bar" }, + { ExceptionMessageTag, exception.Message }, + { ExceptionStackTraceTag, exception.ToString() }, + { ExceptionTypeTag, exception.GetType().ToString() } + }, + events[2].Tags); + + listener.ExceptionRecorder = (Activity activity, Exception exception, ref TagList theTags) => + { + theTags.Add("exception.escaped", "true"); + theTags.Add("exception.message", "Overridden message"); + theTags.Add("exception.stacktrace", "Overridden stacktrace"); + theTags.Add("exception.type", "Overridden type"); + }; + activity.AddException(exception, new TagList { { "hello", "world" } }); + events = activity.Events.ToList(); + Assert.Equal(4, events.Count); + Assert.Equal(ExceptionEventName, events[3].Name); + Assert.Equal(new TagList + { + { "hello", "world" }, + { "exception.escaped", "true" }, + { "exception.message", "Overridden message" }, + { "exception.stacktrace", "Overridden stacktrace" }, + { "exception.type", "Overridden type" } + }, + events[3].Tags); + + ActivityListener listener1 = new ActivityListener(); + listener1.ShouldListenTo = (activitySource) => object.ReferenceEquals(activitySource, aSource); + listener1.Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData; + ActivitySource.AddActivityListener(listener1); + listener1.ExceptionRecorder = (Activity activity, Exception exception, ref TagList theTags) => + { + theTags.Remove(new KeyValuePair("exception.message", "Overridden message")); + theTags.Remove(new KeyValuePair("exception.stacktrace", "Overridden stacktrace")); + theTags.Remove(new KeyValuePair("exception.type", "Overridden type")); + theTags.Add("secondListener", "win"); + }; + activity.AddException(exception, new TagList { { "hello", "world" } }); + events = activity.Events.ToList(); + Assert.Equal(5, events.Count); + Assert.Equal(ExceptionEventName, events[4].Name); + Assert.Equal(new TagList + { + { "hello", "world" }, + { "exception.escaped", "true" }, + { "secondListener", "win" }, + { "exception.message", exception.Message }, + { "exception.stacktrace", exception.ToString() }, + { "exception.type", exception.GetType().ToString() }, + }, + events[4].Tags); + } + [Fact] public void TestIsAllDataRequested() { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs index b292e55f1bcf2..db5bd31d38821 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs @@ -923,6 +923,63 @@ public void ListenerDisposalsTest() }).Dispose(); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void ListenerWithoutMeasurementsCompletedDisposalsTest() + { + RemoteExecutor.Invoke(() => { + Meter meter = new Meter("MinimalListenerDisposalsTest"); + + Counter counter = meter.CreateCounter("Counter"); + UpDownCounter upDownCounter = meter.CreateUpDownCounter("upDownCounter"); + Histogram histogram = meter.CreateHistogram("Histogram"); + ObservableCounter observableCounter = meter.CreateObservableCounter("ObservableCounter", () => new Measurement(10, new KeyValuePair[] { new KeyValuePair("Key", "value")})); + ObservableGauge observableGauge = meter.CreateObservableGauge("ObservableGauge", () => new Measurement(5.7m, new KeyValuePair[] { new KeyValuePair("Key", "value")})); + ObservableUpDownCounter observableUpDownCounter = meter.CreateObservableUpDownCounter("ObservableUpDownCounter", () => new Measurement(-5.7f, new KeyValuePair[] { new KeyValuePair("Key", "value")})); + + MeterListener listener = new MeterListener(); + listener.InstrumentPublished = (theInstrument, theListener) => theListener.EnableMeasurementEvents(theInstrument, theInstrument); + + int count = 0; + + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + listener.SetMeasurementEventCallback((inst, measurement, tags, state) => count++); + + listener.Start(); + + Assert.Equal(0, count); + + counter.Add(1); + Assert.Equal(1, count); + + upDownCounter.Add(-1); + Assert.Equal(2, count); + + histogram.Record(1); + Assert.Equal(3, count); + + listener.RecordObservableInstruments(); + Assert.Equal(6, count); + + listener.Dispose(); + + counter.Add(1); + Assert.Equal(6, count); + + upDownCounter.Add(-1); + Assert.Equal(6, count); + + histogram.Record(1); + Assert.Equal(6, count); + + listener.RecordObservableInstruments(); + Assert.Equal(6, count); + }).Dispose(); + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void MultipleListenersTest() { diff --git a/src/libraries/System.Diagnostics.EventLog/README.md b/src/libraries/System.Diagnostics.EventLog/README.md index 6210227641107..5f5a359a9486e 100644 --- a/src/libraries/System.Diagnostics.EventLog/README.md +++ b/src/libraries/System.Diagnostics.EventLog/README.md @@ -2,7 +2,7 @@ This assembly exposes the System.Diagnostics.EventLog class, which allows the applications to use the Windows event log service. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventlog. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.diagnostics.eventlog. ## Contribution Bar diff --git a/src/libraries/System.Diagnostics.EventLog/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.EventLog/src/CompatibilitySuppressions.xml index 81018913a46eb..6724110f6b1d0 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Diagnostics.EventLog/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0004 diff --git a/src/libraries/System.Diagnostics.EventLog/src/Messages/readme.md b/src/libraries/System.Diagnostics.EventLog/src/Messages/readme.md index 9ea4b4d69b262..dc93c72ed136a 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/Messages/readme.md +++ b/src/libraries/System.Diagnostics.EventLog/src/Messages/readme.md @@ -1,6 +1,6 @@ These files are used to produce an Event Message File. -For more information see https://docs.microsoft.com/en-us/windows/win32/eventlog/message-files. +For more information see https://learn.microsoft.com/windows/win32/eventlog/message-files. The design of the EventLog class is to allow for the registration of event sources without specifying message files. diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/README.md b/src/libraries/System.Diagnostics.PerformanceCounter/README.md index ed5ad148dc78c..8a281ce3f491e 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/README.md +++ b/src/libraries/System.Diagnostics.PerformanceCounter/README.md @@ -2,7 +2,7 @@ This assembly exposes the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.performancecounter. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.diagnostics.performancecounter. ## Contribution Bar diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml index 9633772cc6893..70c5f8d65c7d8 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index dc10150408274..6054597a37fb6 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -860,7 +860,7 @@ private static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHand private static string GetEnvironmentVariablesBlock(DictionaryWrapper sd) { - // https://docs.microsoft.com/en-us/windows/win32/procthread/changing-environment-variables + // https://learn.microsoft.com/windows/win32/procthread/changing-environment-variables // "All strings in the environment block must be sorted alphabetically by name. The sort is // case-insensitive, Unicode order, without regard to locale. Because the equal sign is a // separator, it must not be used in the name of an environment variable." diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs index 26174ab7aef20..55a38beec9089 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs @@ -571,7 +571,7 @@ public void TestClosingStreamsAsyncDoesNotThrow() p.StartInfo.RedirectStandardError = true; // On netfx, the handler is called once with the Data as null, even if the process writes nothing to the pipe. - // That behavior is documented here https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.datareceivedeventhandler + // That behavior is documented here https://learn.microsoft.com/dotnet/api/system.diagnostics.datareceivedeventhandler p.Start(); p.BeginOutputReadLine(); diff --git a/src/libraries/System.Diagnostics.TraceSource/README.md b/src/libraries/System.Diagnostics.TraceSource/README.md index e7f8b2c0e274f..d01ec9160ee79 100644 --- a/src/libraries/System.Diagnostics.TraceSource/README.md +++ b/src/libraries/System.Diagnostics.TraceSource/README.md @@ -2,7 +2,7 @@ This assembly exposes the System.Diagnostics.TraceSource class, which provides a set of methods and properties that enable applications to trace the execution of code and associate trace messages with their source. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracesource. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.diagnostics.tracesource. ## Contribution Bar diff --git a/src/libraries/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md b/src/libraries/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md index 26ce253a585c4..f77d7a2cd556d 100644 --- a/src/libraries/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md +++ b/src/libraries/System.Diagnostics.Tracing/documentation/EventCounterTutorial.md @@ -6,7 +6,7 @@ For events that happen very frequently (for example, if it happens every few mil When dealing with large number of events, knowing the measure per event is not very useful either. Most of the time all we need is just some statistics out of it. So we could crank the statistics within the process itself and then write an event once in a while to report the statistics, that's what `EventCounter` will do for us. Let's take a look at an example how to do this in `Microsoft.Diagnostics.Tracing.EventSource`. -In the sequel, we assume you are familiar with the basic `EventSource` usage, if you don't, please refer to [Vance's excellent blog](https://docs.microsoft.com/en-us/archive/blogs/vancem/introduction-tutorial-logging-etw-events-in-c-system-diagnostics-tracing-eventsource) on that. +In the sequel, we assume you are familiar with the basic `EventSource` usage, if you don't, please refer to [Vance's excellent blog](https://learn.microsoft.com/archive/blogs/vancem/introduction-tutorial-logging-etw-events-in-c-system-diagnostics-tracing-eventsource) on that. Without further ado, here is an example on how to use the `EventCounter` diff --git a/src/libraries/System.DirectoryServices/src/CompatibilitySuppressions.xml b/src/libraries/System.DirectoryServices/src/CompatibilitySuppressions.xml index 2961a274f4a12..244f91b260377 100644 --- a/src/libraries/System.DirectoryServices/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.DirectoryServices/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/System.Drawing.Primitives/README.md b/src/libraries/System.Drawing.Primitives/README.md index 435c7cfdb0793..70730552777ca 100644 --- a/src/libraries/System.Drawing.Primitives/README.md +++ b/src/libraries/System.Drawing.Primitives/README.md @@ -1,9 +1,9 @@ # System.Drawing.Primitives -This assembly exposes fundamental drawing APIs such as [`Point`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.point) and [`Size`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.size) which don't require access to native GDI+ libraries. +This assembly exposes fundamental drawing APIs such as [`Point`](https://learn.microsoft.com/dotnet/api/system.drawing.point) and [`Size`](https://learn.microsoft.com/dotnet/api/system.drawing.size) which don't require access to native GDI+ libraries. `System.Drawing.Primitives` supports not just Windows, but all officially supported platforms. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.drawing. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.drawing. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../README.md#primary-bar) diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/ColorTranslator.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/ColorTranslator.cs index b8fc7866abe24..ab679a7cde3dd 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/ColorTranslator.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/ColorTranslator.cs @@ -44,7 +44,7 @@ public static int ToOle(Color c) // also updating the runtime. // This method converts Color to an OLE_COLOR. - // https://docs.microsoft.com/openspecs/office_file_formats/ms-oforms/4b8f4be0-3fff-4e42-9fc1-b9fd00251e8e + // https://learn.microsoft.com/openspecs/office_file_formats/ms-oforms/4b8f4be0-3fff-4e42-9fc1-b9fd00251e8e if (c.IsKnownColor && c.IsSystemColor) { diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index d54cb5a954713..1f861f7d715a1 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -469,7 +469,7 @@ internal void WriteCentralDirectoryFileHeader() bool zip64Needed = false; - if (SizesTooLarge() + if (AreSizesTooLarge #if DEBUG_FORCE_ZIP64 || _archive._forceZip64 #endif @@ -490,7 +490,7 @@ internal void WriteCentralDirectoryFileHeader() } - if (_offsetOfLocalHeader > uint.MaxValue + if (IsOffsetTooLarge #if DEBUG_FORCE_ZIP64 || _archive._forceZip64 #endif @@ -692,7 +692,7 @@ private WrappedStream OpenInWriteMode() if (_everOpenedForWrite) throw new IOException(SR.CreateModeWriteOnceAndOneEntryAtATime); - // we assume that if another entry grabbed the archive stream, that it set this entry's _everOpenedForWrite property to true by calling WriteLocalFileHeaderIfNeeed + // we assume that if another entry grabbed the archive stream, that it set this entry's _everOpenedForWrite property to true by calling WriteLocalFileHeaderAndDataIfNeeded _archive.DebugAssertIsStillArchiveStreamOwner(this); _everOpenedForWrite = true; @@ -797,7 +797,7 @@ private bool IsOpenable(bool needToUncompress, bool needToLoadIntoMemory, out st return true; } - private bool SizesTooLarge() => _compressedSize > uint.MaxValue || _uncompressedSize > uint.MaxValue; + private bool AreSizesTooLarge => _compressedSize > uint.MaxValue || _uncompressedSize > uint.MaxValue; private static CompressionLevel MapCompressionLevel(BitFlagValues generalPurposeBitFlag, CompressionMethodValues compressionMethod) { @@ -839,6 +839,10 @@ private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPu return (BitFlagValues)(((int)generalPurposeBitFlag & ~0x6) | deflateCompressionOptions); } + private bool IsOffsetTooLarge => _offsetOfLocalHeader > uint.MaxValue; + + private bool ShouldUseZIP64 => AreSizesTooLarge || IsOffsetTooLarge; + // return value is true if we allocated an extra field for 64 bit headers, un/compressed size private bool WriteLocalFileHeader(bool isEmptyFile) { @@ -853,6 +857,9 @@ private bool WriteLocalFileHeader(bool isEmptyFile) bool zip64Used = false; uint compressedSizeTruncated, uncompressedSizeTruncated; + // save offset + _offsetOfLocalHeader = writer.BaseStream.Position; + // if we already know that we have an empty file don't worry about anything, just do a straight shot of the header if (isEmptyFile) { @@ -880,7 +887,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile) { // We are in seekable mode so we will not need to write a data descriptor _generalPurposeBitFlag &= ~BitFlagValues.DataDescriptor; - if (SizesTooLarge() + if (ShouldUseZIP64 #if DEBUG_FORCE_ZIP64 || (_archive._forceZip64 && _archive.Mode == ZipArchiveMode.Update) #endif @@ -905,9 +912,6 @@ private bool WriteLocalFileHeader(bool isEmptyFile) } } - // save offset - _offsetOfLocalHeader = writer.BaseStream.Position; - // calculate extra field. if zip64 stuff + original extraField aren't going to fit, dump the original extraField, because this is more important int bigExtraFieldLength = (zip64Used ? zip64ExtraField.TotalSize : 0) + (_lhUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_lhUnknownExtraFields) : 0); @@ -1004,7 +1008,7 @@ private void WriteCrcAndSizesInLocalHeader(bool zip64HeaderUsed) long finalPosition = _archive.ArchiveStream.Position; BinaryWriter writer = new BinaryWriter(_archive.ArchiveStream); - bool zip64Needed = SizesTooLarge() + bool zip64Needed = ShouldUseZIP64 #if DEBUG_FORCE_ZIP64 || _archive._forceZip64 #endif @@ -1088,7 +1092,7 @@ private void WriteDataDescriptor() writer.Write(ZipLocalFileHeader.DataDescriptorSignature); writer.Write(_crc32); - if (SizesTooLarge()) + if (AreSizesTooLarge) { writer.Write(_compressedSize); writer.Write(_uncompressedSize); diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs index d240a176b2b7a..b4623e653af84 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs @@ -1,48 +1,129 @@ // 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.Reflection; using Xunit; -namespace System.IO.Compression.Tests +namespace System.IO.Compression.Tests; + +[Collection(nameof(DisableParallelization))] +public class zip_LargeFiles : ZipFileTestBase { - [Collection(nameof(DisableParallelization))] - public class zip_LargeFiles : ZipFileTestBase + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [OuterLoop("It requires almost 12 GB of free disk space")] + public static void UnzipOver4GBZipFile() { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes - [OuterLoop("It requires almost 12 GB of free disk space")] - public static void UnzipOver4GBZipFile() + byte[] buffer = GC.AllocateUninitializedArray(1_000_000_000); // 1 GB + + string zipArchivePath = Path.Combine(Path.GetTempPath(), "over4GB.zip"); + DirectoryInfo tempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "over4GB")); + + try + { + for (byte i = 0; i < 6; i++) + { + File.WriteAllBytes(Path.Combine(tempDir.FullName, $"{i}.test"), buffer); + } + + ZipFile.CreateFromDirectory(tempDir.FullName, zipArchivePath, CompressionLevel.NoCompression, includeBaseDirectory: false); + + using ZipArchive zipArchive = ZipFile.OpenRead(zipArchivePath); + foreach (ZipArchiveEntry entry in zipArchive.Entries) + { + using Stream entryStream = entry.Open(); + + Assert.True(entryStream.CanRead); + Assert.Equal(buffer.Length, entryStream.Length); + } + } + finally { - byte[] buffer = GC.AllocateUninitializedArray(1_000_000_000); // 1 GB + File.Delete(zipArchivePath); + + tempDir.Delete(recursive: true); + } + } + + private static void FillWithHardToCompressData(byte[] buffer) + { + Random.Shared.NextBytes(buffer); + } - string zipArchivePath = Path.Combine(Path.GetTempPath(), "over4GB.zip"); - DirectoryInfo tempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "over4GB")); + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [OuterLoop("It requires 5~6 GB of free disk space and a lot of CPU time for compressed tests")] + [InlineData(false)] + [InlineData(true)] + public static void CheckZIP64VersionIsSet_ForSmallFilesAfterBigFiles(bool isCompressed) + { + // issue #94899 + + CompressionLevel compressLevel = isCompressed ? CompressionLevel.Optimal : CompressionLevel.NoCompression; + byte[] smallBuffer = GC.AllocateUninitializedArray(1000); + byte[] largeBuffer = GC.AllocateUninitializedArray(1_000_000_000); // ~1 GB + string zipArchivePath = Path.Combine(Path.GetTempPath(), "over4GB.zip"); + string LargeFileName = "largefile"; + string SmallFileName = "smallfile"; + uint ZipLocalFileHeader_OffsetToVersionFromHeaderStart = 4; + ushort Zip64Version = 45; - try + try + { + using FileStream fs = File.Open(zipArchivePath, FileMode.Create, FileAccess.ReadWrite); + + // Create + using (ZipArchive archive = new(fs, ZipArchiveMode.Create, true)) { - for (byte i = 0; i < 6; i++) + ZipArchiveEntry file = archive.CreateEntry(LargeFileName, compressLevel); + + using (Stream stream = file.Open()) { - File.WriteAllBytes(Path.Combine(tempDir.FullName, $"{i}.test"), buffer); + // Write 5GB of data + for (var i = 0; i < 5; i++) + { + if (isCompressed) + { + FillWithHardToCompressData(largeBuffer); + } + + stream.Write(largeBuffer); + } } - ZipFile.CreateFromDirectory(tempDir.FullName, zipArchivePath, CompressionLevel.NoCompression, includeBaseDirectory: false); + file = archive.CreateEntry(SmallFileName, compressLevel); - using ZipArchive zipArchive = ZipFile.OpenRead(zipArchivePath); - foreach (ZipArchiveEntry entry in zipArchive.Entries) + using (Stream stream = file.Open()) { - using Stream entryStream = entry.Open(); - - Assert.True(entryStream.CanRead); - Assert.Equal(buffer.Length, entryStream.Length); + stream.Write(smallBuffer); } } - finally + + fs.Position = 0; + + // Validate + using (ZipArchive archive = new(fs, ZipArchiveMode.Read)) { - File.Delete(zipArchivePath); + using var reader = new BinaryReader(fs); - tempDir.Delete(recursive: true); + FieldInfo offsetOfLHField = typeof(ZipArchiveEntry).GetField("_offsetOfLocalHeader", BindingFlags.NonPublic | BindingFlags.Instance); + + if (offsetOfLHField is null || offsetOfLHField.FieldType != typeof(long)) + { + Assert.Fail("Cannot find the private field of _offsetOfLocalHeader in ZipArchiveEntry or the type is not long. Code may be changed after the test is written."); + } + + foreach (ZipArchiveEntry entry in archive.Entries) + { + fs.Position = (long)offsetOfLHField.GetValue(entry) + ZipLocalFileHeader_OffsetToVersionFromHeaderStart; + ushort versionNeeded = reader.ReadUInt16(); + + // Version is not ZIP64 for files with Local Header at >4GB offset. + Assert.Equal(Zip64Version, versionNeeded); + } } } + finally + { + File.Delete(zipArchivePath); + } } } diff --git a/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs b/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs index cecec0f38bbb6..f1fcfb677b905 100644 --- a/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs +++ b/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs @@ -213,7 +213,7 @@ public static DirectoryInfo CreateDirectory(this DirectorySecurity directorySecu } // In the context of a FileStream, the only ACCESS_MASK ACE rights we care about are reading/writing data and the generic read/write rights. - // See: https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask + // See: https://learn.microsoft.com/windows/win32/secauthz/access-mask private static FileAccess GetFileAccessFromRights(FileSystemRights rights) { FileAccess access = 0; diff --git a/src/libraries/System.IO.FileSystem.Watcher/src/System/HResults.cs b/src/libraries/System.IO.FileSystem.Watcher/src/System/HResults.cs index 59b7ce8ab2574..f3fa06865eb3d 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/src/System/HResults.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/src/System/HResults.cs @@ -5,7 +5,7 @@ /* These HRESULTs are used for mapping managed exceptions to COM error codes and vice versa through COM Interop. For background on COM error codes see -https://docs.microsoft.com/en-us/windows/desktop/com/com-error-codes. +https://learn.microsoft.com/windows/desktop/com/com-error-codes. FACILITY_URT is defined as 0x13 (0x8013xxxx). The facility range is reserved for the .NET Framework SDK teams. diff --git a/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs new file mode 100644 index 0000000000000..c073c09b8f92f --- /dev/null +++ b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO.Compression; +using System.Net.Mime; +using System.Reflection; +using Xunit; + +namespace System.IO.Packaging.Tests; + +public partial class LargeFilesTests +{ + private static void FillWithHardToCompressData(byte[] buffer) + { + Random.Shared.NextBytes(buffer); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [InlineData(false)] + [InlineData(true)] + [OuterLoop("It requires 5~6 GB of free disk space and a lot of CPU time for compressed tests")] + public static void CheckZIP64VersionIsSet_ForSmallFilesAfterBigFiles(bool isCompressed) + { + // issue #94899 + + CompressionOption compressionOption = isCompressed ? CompressionOption.Normal : CompressionOption.NotCompressed; + byte[] smallBuffer = GC.AllocateUninitializedArray(1000); + byte[] largeBuffer = GC.AllocateUninitializedArray(1_000_000_000); // ~1 GB + string zipArchivePath = Path.Combine(Path.GetTempPath(), "over4GB.zip"); + Uri largePartUri = PackUriHelper.CreatePartUri(new Uri("large.bin", UriKind.Relative)); + Uri smallPartUri = PackUriHelper.CreatePartUri(new Uri("small.bin", UriKind.Relative)); + uint ZipLocalFileHeader_OffsetToVersionFromHeaderStart = 4; + ushort Zip64Version = 45; + + try + { + using FileStream fs = File.Open(zipArchivePath, FileMode.Create, FileAccess.ReadWrite); + + // Create + using (Package package = Package.Open(fs, FileMode.Create, FileAccess.Write)) + { + PackagePart partLarge = package.CreatePart(largePartUri, MediaTypeNames.Application.Octet, compressionOption); + + using (Stream streamLarge = partLarge.GetStream()) + { + // Write 5GB of data + + for (var i = 0; i < 5; i++) + { + if (isCompressed) + { + FillWithHardToCompressData(largeBuffer); + } + + streamLarge.Write(largeBuffer); + } + } + + PackagePart partSmall = package.CreatePart(smallPartUri, MediaTypeNames.Application.Octet, compressionOption); + + using (Stream streamSmall = partSmall.GetStream()) + { + streamSmall.Write(smallBuffer); + } + } + + + fs.Position = 0; + + // Validate + using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Read)) + { + using var reader = new BinaryReader(fs); + + FieldInfo offsetOfLHField = typeof(ZipArchiveEntry).GetField("_offsetOfLocalHeader", BindingFlags.NonPublic | BindingFlags.Instance); + + if (offsetOfLHField is null || offsetOfLHField.FieldType != typeof(long)) + { + Assert.Fail("Cannot find the private field of _offsetOfLocalHeader in ZipArchiveEntry or the type is not long. Code may be changed after the test is written."); + } + + foreach (ZipArchiveEntry entry in archive.Entries) + { + fs.Position = (long)offsetOfLHField.GetValue(entry) + ZipLocalFileHeader_OffsetToVersionFromHeaderStart; + ushort versionNeeded = reader.ReadUInt16(); + + // Version is not ZIP64 for files with Local Header at >4GB offset. + Assert.Equal(Zip64Version, versionNeeded); + } + } + } + finally + { + File.Delete(zipArchivePath); + } + } +} diff --git a/src/libraries/System.IO.Packaging/tests/LargeFilesTests.cs b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.cs new file mode 100644 index 0000000000000..e5e2350d7632b --- /dev/null +++ b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.Packaging.Tests; + +[Collection(nameof(DisableParallelization))] +public partial class LargeFileTests +{ + [Fact] + [OuterLoop] + public void VeryLargePart() + { + // FileAccess.Write is important, this tells ZipPackage to open the underlying ZipArchive in + // ZipArchiveMode.Create mode as opposed to ZipArchiveMode.Update + // When ZipArchive is opened in Create it will write entries directly to the zip stream + // When ZipArchive is opened in Update it will write uncompressed data to memory until + // the archive is closed. + using (Stream stream = new MemoryStream()) + { + Uri partUri = PackUriHelper.CreatePartUri(new Uri("test.bin", UriKind.Relative)); + + // should compress *very well* + byte[] buffer = new byte[1024 * 1024]; + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)(i % 2); + } + + const long SizeInMb = 6 * 1024; // 6GB + long totalLength = SizeInMb * buffer.Length; + + // issue on .NET Framework we cannot use FileAccess.Write on a ZipArchive + using (Package package = Package.Open(stream, FileMode.Create, PlatformDetection.IsNetFramework ? FileAccess.ReadWrite : FileAccess.Write)) + { + PackagePart part = package.CreatePart(partUri, + System.Net.Mime.MediaTypeNames.Application.Octet, + CompressionOption.Fast); + + + using (Stream partStream = part.GetStream()) + { + for (long i = 0; i < SizeInMb; i++) + { + partStream.Write(buffer, 0, buffer.Length); + } + } + } + + // reopen for read and make sure we can get the part length & data matches + stream.Seek(0, SeekOrigin.Begin); + using (Package readPackage = Package.Open(stream)) + { + PackagePart part = readPackage.GetPart(partUri); + + using (Stream partStream = part.GetStream()) + { + Assert.Equal(totalLength, partStream.Length); + byte[] readBuffer = new byte[buffer.Length]; + for (long i = 0; i < SizeInMb; i++) + { + int totalRead = 0; + while (totalRead < readBuffer.Length) + { + int actualRead = partStream.Read(readBuffer, totalRead, readBuffer.Length - totalRead); + Assert.InRange(actualRead, 1, readBuffer.Length - totalRead); + totalRead += actualRead; + } + + Assert.Equal(readBuffer.Length, totalRead); + Assert.True(buffer.AsSpan().SequenceEqual(readBuffer)); + } + } + } + } + } +} diff --git a/src/libraries/System.IO.Packaging/tests/System.IO.Packaging.Tests.csproj b/src/libraries/System.IO.Packaging/tests/System.IO.Packaging.Tests.csproj index 78bc14a789b08..c7bdadf60c902 100644 --- a/src/libraries/System.IO.Packaging/tests/System.IO.Packaging.Tests.csproj +++ b/src/libraries/System.IO.Packaging/tests/System.IO.Packaging.Tests.csproj @@ -3,10 +3,14 @@ $(NetCoreAppCurrent);$(NetFrameworkCurrent) + + + + diff --git a/src/libraries/System.IO.Packaging/tests/Tests.cs b/src/libraries/System.IO.Packaging/tests/Tests.cs index 2d814bdde5051..fe5c0374b0272 100644 --- a/src/libraries/System.IO.Packaging/tests/Tests.cs +++ b/src/libraries/System.IO.Packaging/tests/Tests.cs @@ -3771,74 +3771,6 @@ private void ForEachPartWithFileName(Package package, Action - + CP0001 diff --git a/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml b/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml index 6cd6653b9e03f..2524d0e9645e5 100644 --- a/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs index 288d01c185dbf..fb11d87ce5f2c 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs @@ -321,7 +321,7 @@ private bool ExpandoContainsKey(string key) // that uses DebuggerTypeProxy, instead of defining a generic debug view type and // using different instantiations. The reason for this is that support for generics // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only - // open types (from MSDN https://docs.microsoft.com/en-us/visualstudio/debugger/using-debuggertypeproxy-attribute). + // open types (from MSDN https://learn.microsoft.com/visualstudio/debugger/using-debuggertypeproxy-attribute). private sealed class KeyCollectionDebugView { private readonly ICollection _collection; @@ -460,7 +460,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() // that uses DebuggerTypeProxy, instead of defining a generic debug view type and // using different instantiations. The reason for this is that support for generics // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only - // open types (from MSDN https://docs.microsoft.com/en-us/visualstudio/debugger/using-debuggertypeproxy-attribute). + // open types (from MSDN https://learn.microsoft.com/visualstudio/debugger/using-debuggertypeproxy-attribute). private sealed class ValueCollectionDebugView { private readonly ICollection _collection; diff --git a/src/libraries/System.Linq.Queryable/README.md b/src/libraries/System.Linq.Queryable/README.md index c23f7e0abdffb..930fb1ea91bcb 100644 --- a/src/libraries/System.Linq.Queryable/README.md +++ b/src/libraries/System.Linq.Queryable/README.md @@ -2,7 +2,7 @@ Provides a set of extension methods for querying data structures that implement `IQueryable`. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.linq. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.linq. ## Contribution Bar diff --git a/src/libraries/System.Memory.Data/src/CompatibilitySuppressions.xml b/src/libraries/System.Memory.Data/src/CompatibilitySuppressions.xml index 9e798aa1ff34e..27c7ce330247e 100644 --- a/src/libraries/System.Memory.Data/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Memory.Data/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0015 diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs index 179cb829ef52d..f568ef2f684ac 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs @@ -29,7 +29,15 @@ private JsonContent( Value = inputValue; _typeInfo = jsonTypeInfo; - Headers.ContentType = mediaType ?? JsonHelpers.GetDefaultMediaType(); + + if (mediaType is not null) + { + Headers.ContentType = mediaType; + } + else + { + Headers.TryAddWithoutValidation("Content-Type", JsonHelpers.DefaultMediaType); + } } /// diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs index 63caa8dc81b04..fbf6644558521 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs @@ -25,7 +25,7 @@ internal static JsonTypeInfo GetJsonTypeInfo(Type type, JsonSerializerOptions? o return options.GetTypeInfo(type); } - internal static MediaTypeHeaderValue GetDefaultMediaType() => new("application/json") { CharSet = "utf-8" }; + internal const string DefaultMediaType = "application/json; charset=utf-8"; internal static Encoding? GetEncoding(HttpContent content) { diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx index 4fcb089cc506d..82e7445d67e74 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/Resources/Strings.resx @@ -132,4 +132,7 @@ An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. + + Request version value must be one of 1.0, 1.1, 2.0, or 3.0. + diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 653ec96c1bb3d..86c893169270b 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -39,6 +39,7 @@ public class WinHttpHandler : HttpMessageHandler // However, these are not part of 'netstandard'. WinHttpHandler currently builds against // 'netstandard' so we need to add these definitions here. internal static readonly Version HttpVersion20 = new Version(2, 0); + internal static readonly Version HttpVersion30 = new Version(3, 0); internal static readonly Version HttpVersionUnknown = new Version(0, 0); private static readonly TimeSpan s_maxTimeout = TimeSpan.FromMilliseconds(int.MaxValue); @@ -80,7 +81,7 @@ private Func< private TimeSpan _receiveDataTimeout = TimeSpan.FromSeconds(30); // Using OS defaults for "Keep-alive timeout" and "keep-alive interval" - // as documented in https://docs.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals#remarks + // as documented in https://learn.microsoft.com/windows/win32/winsock/sio-keepalive-vals#remarks private TimeSpan _tcpKeepAliveTime = TimeSpan.FromHours(2); private TimeSpan _tcpKeepAliveInterval = TimeSpan.FromSeconds(1); private bool _tcpKeepAliveEnabled; @@ -877,6 +878,13 @@ private async Task StartRequestAsync(WinHttpRequestState state) return; } + if (state.RequestMessage.Version != HttpVersion.Version10 && state.RequestMessage.Version != HttpVersion.Version11 + && state.RequestMessage.Version != HttpVersion20 && state.RequestMessage.Version != HttpVersion30) + { + state.Tcs.TrySetException(new NotSupportedException(SR.net_http_unsupported_version)); + return; + } + Task? sendRequestBodyTask = null; SafeWinHttpHandle? connectHandle = null; try diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj index 607b3ea038910..25c0dcc9cfb7c 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj @@ -32,6 +32,8 @@ Link="Common\System\Net\EventSourceTestLogging.cs" /> + HTTP 1.0 does not support chunking. - Request HttpVersion 0.X is not supported. Use 1.0 or above. + Request version value must be one of 1.0, 1.1, 2.0, or 3.0. An attempt was made to move the position before the beginning of the stream. @@ -576,7 +576,10 @@ The HTTP/1.1 response chunk was too large. - + Failed to establish web socket connection over HTTP/2 because extended CONNECT is not supported. Try to downgrade the request version to HTTP/1.1. + + The HTTP/2 server didn't respond to a ping request within the configured KeepAlivePingDelay. + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index e91ccefff630f..74b6fc84c3256 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -2136,7 +2136,7 @@ private void VerifyKeepAlive() break; case KeepAliveState.PingSent: if (now > _keepAlivePingTimeoutTimestamp) - ThrowProtocolError(); + ThrowProtocolError(Http2ProtocolErrorCode.ProtocolError, SR.net_ping_request_timed_out); break; default: Debug.Fail($"Unexpected keep alive state ({_keepAliveState})"); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 6227738dc3d30..47d210a6ba838 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -610,7 +610,7 @@ protected internal override Task SendAsync(HttpRequestMessa private static Exception? ValidateAndNormalizeRequest(HttpRequestMessage request) { - if (request.Version.Major == 0) + if (request.Version != HttpVersion.Version10 && request.Version != HttpVersion.Version11 && request.Version != HttpVersion.Version20 && request.Version != HttpVersion.Version30) { return new NotSupportedException(SR.net_http_unsupported_version); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index a3a83db4ea6f1..9059a05d49f6b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -49,9 +49,9 @@ public void EventSource_ExistsWithCorrectId() // with those tests. [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceLogging() + public async Task SendAsync_ExpectedDiagnosticSourceLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { HttpRequestMessage requestLogged = null; HttpResponseMessage responseLogged = null; @@ -109,13 +109,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.False(exceptionLogged, "Exception was logged for successful request"); Assert.False(activityLogged, "HttpOutReq was logged while HttpOutReq logging was disabled"); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceNoLogging() + public async Task SendAsync_ExpectedDiagnosticSourceNoLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool requestLogged = false; bool responseLogged = false; @@ -160,20 +160,20 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.False(responseLogged, "Response was logged while logging disabled."); Assert.False(activityStopLogged, "HttpRequestOut.Stop was logged while logging disabled."); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(false)] [InlineData(true)] - public void SendAsync_HttpTracingEnabled_Succeeds(bool useSsl) + public async Task SendAsync_HttpTracingEnabled_Succeeds(bool useSsl) { if (useSsl && UseVersion == HttpVersion.Version20 && !PlatformDetection.SupportsAlpn) { return; } - RemoteExecutor.Invoke(async (useVersion, useSsl, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, useSsl, testAsync) => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.Http", EventLevel.Verbose)) { @@ -194,13 +194,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( ev => ev.EventId == 0); // make sure there are no event source error messages Assert.InRange(events.Count, 1, int.MaxValue); } - }, UseVersion.ToString(), useSsl.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), useSsl.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticExceptionLogging() + public async Task SendAsync_ExpectedDiagnosticExceptionLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { Exception exceptionLogged = null; @@ -232,13 +232,13 @@ public void SendAsync_ExpectedDiagnosticExceptionLogging() Assert.Same(ex, exceptionLogged); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticCancelledLogging() + public async Task SendAsync_ExpectedDiagnosticCancelledLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { TaskCompletionSource responseLoggedTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); TaskCompletionSource activityStopTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -284,15 +284,15 @@ await server.AcceptConnectionAsync(async connection => }); }); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(ActivityIdFormat.Hierarchical)] [InlineData(ActivityIdFormat.W3C)] - public void SendAsync_ExpectedDiagnosticSourceActivityLogging(ActivityIdFormat idFormat) + public async Task SendAsync_ExpectedDiagnosticSourceActivityLogging(ActivityIdFormat idFormat) { - RemoteExecutor.Invoke(async (useVersion, testAsync, idFormatString) => + await RemoteExecutor.Invoke(async (useVersion, testAsync, idFormatString) => { ActivityIdFormat idFormat = Enum.Parse(idFormatString); @@ -376,13 +376,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.False(exceptionLogged, "Exception was logged for successful request"); Assert.False(responseLogged, "Response was logged when Activity logging was enabled."); } - }, UseVersion.ToString(), TestAsync.ToString(), idFormat.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString(), idFormat.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceActivityLogging_InvalidBaggage() + public async Task SendAsync_ExpectedDiagnosticSourceActivityLogging_InvalidBaggage() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool exceptionLogged = false; @@ -432,13 +432,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.False(exceptionLogged, "Exception was logged for successful request"); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteHeader() + public async Task SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteHeader() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool activityStartLogged = false; @@ -486,13 +486,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.True(activityStartLogged, "HttpRequestOut.Start was not logged."); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteW3CTraceParentHeader() + public async Task SendAsync_ExpectedDiagnosticSourceActivityLoggingDoesNotOverwriteW3CTraceParentHeader() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool activityStartLogged = false; @@ -544,13 +544,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.True(activityStartLogged, "HttpRequestOut.Start was not logged."); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceUrlFilteredActivityLogging() + public async Task SendAsync_ExpectedDiagnosticSourceUrlFilteredActivityLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool activityStartLogged = false; bool activityStopLogged = false; @@ -588,13 +588,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.False(activityStartLogged, "HttpRequestOut.Start was logged while URL disabled."); Assert.False(activityStopLogged, "HttpRequestOut.Stop was logged while URL disabled."); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticExceptionActivityLogging() + public async Task SendAsync_ExpectedDiagnosticExceptionActivityLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { Exception exceptionLogged = null; @@ -627,13 +627,13 @@ public void SendAsync_ExpectedDiagnosticExceptionActivityLogging() Assert.Same(ex, exceptionLogged); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSynchronousExceptionActivityLogging() + public async Task SendAsync_ExpectedDiagnosticSynchronousExceptionActivityLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { Exception exceptionLogged = null; @@ -704,13 +704,13 @@ public void SendAsync_ExpectedDiagnosticSynchronousExceptionActivityLogging() Assert.Same(exceptionLogged, exception); } } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticSourceNewAndDeprecatedEventsLogging() + public async Task SendAsync_ExpectedDiagnosticSourceNewAndDeprecatedEventsLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool requestLogged = false; bool activityStartLogged = false; @@ -755,13 +755,13 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.True(requestLogged, "Request was not logged."); Assert.True(activityStopLogged, "HttpRequestOut.Stop was not logged."); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendAsync_ExpectedDiagnosticExceptionOnlyActivityLogging() + public async Task SendAsync_ExpectedDiagnosticExceptionOnlyActivityLogging() { - RemoteExecutor.Invoke(async (useVersion, testAsync) => + await RemoteExecutor.Invoke(async (useVersion, testAsync) => { bool activityLogged = false; Exception exceptionLogged = null; @@ -793,7 +793,7 @@ public void SendAsync_ExpectedDiagnosticExceptionOnlyActivityLogging() Assert.Same(ex, exceptionLogged); Assert.False(activityLogged, "HttpOutReq was logged when logging was disabled"); } - }, UseVersion.ToString(), TestAsync.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); } public static IEnumerable UseSocketsHttpHandler_WithIdFormat_MemberData() @@ -900,9 +900,9 @@ await GetFactoryForVersion(UseVersion).CreateServerAsync(async (redirectServer, [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(true)] [InlineData(false)] - public void SendAsync_SuppressedGlobalStaticPropagationNoListenerAppCtx(bool switchValue) + public async Task SendAsync_SuppressedGlobalStaticPropagationNoListenerAppCtx(bool switchValue) { - RemoteExecutor.Invoke(async (useVersion, testAsync, switchValue) => + await RemoteExecutor.Invoke(async (useVersion, testAsync, switchValue) => { AppContext.SetSwitch(EnableActivityPropagationAppCtxSettingName, bool.Parse(switchValue)); @@ -917,7 +917,7 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.Equal(bool.Parse(switchValue), request.Headers.Contains(headerName)); }, async server => await server.HandleRequestAsync()); - }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString(), switchValue.ToString()).DisposeAsync(); } public static IEnumerable SocketsHttpHandlerPropagators_WithIdFormat_MemberData() @@ -987,11 +987,11 @@ public static IEnumerable SocketsHttpHandler_ActivityCreation_MemberDa [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(SocketsHttpHandler_ActivityCreation_MemberData))] - public void SendAsync_ActivityIsCreatedIfRequested(bool currentActivitySet, bool? diagnosticListenerActivityEnabled, bool? activitySourceCreatesActivity) + public async Task SendAsync_ActivityIsCreatedIfRequested(bool currentActivitySet, bool? diagnosticListenerActivityEnabled, bool? activitySourceCreatesActivity) { string parameters = $"{currentActivitySet},{diagnosticListenerActivityEnabled},{activitySourceCreatesActivity}"; - RemoteExecutor.Invoke(async (useVersion, testAsync, parametersString) => + await RemoteExecutor.Invoke(async (useVersion, testAsync, parametersString) => { bool?[] parameters = parametersString.Split(',').Select(p => p.Length == 0 ? (bool?)null : bool.Parse(p)).ToArray(); bool currentActivitySet = parameters[0].Value; @@ -1071,7 +1071,7 @@ await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( Assert.Equal(activitySourceCreatesActivity.HasValue, madeASamplingDecision); Assert.Equal(diagnosticListenerActivityEnabled.HasValue, listenerCallbackWasCalled); - }, UseVersion.ToString(), TestAsync.ToString(), parameters).Dispose(); + }, UseVersion.ToString(), TestAsync.ToString(), parameters).DisposeAsync(); } private static T GetProperty(object obj, string propertyName) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs index d6a53a33e2342..f71efde663105 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs @@ -8,8 +8,6 @@ using System.Net.Test.Common; using System.Net.Quic; -using Microsoft.DotNet.XUnitExtensions; - namespace System.Net.Http.Functional.Tests { public abstract class HttpClientHandler_AltSvc_Test : HttpClientHandlerTestBase @@ -29,15 +27,10 @@ private HttpClient CreateHttpClient(Version version) return client; } - [ConditionalTheory] + [Theory] [MemberData(nameof(AltSvcHeaderUpgradeVersions))] public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overrideHost) { - if (UseVersion == HttpVersion30 && fromVersion == HttpVersion.Version11 && overrideHost) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); - } - // The test makes a request to a HTTP/1 or HTTP/2 server first, which supplies an Alt-Svc header pointing to the second server. using GenericLoopbackServer firstServer = fromVersion.Major switch @@ -78,14 +71,9 @@ public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overri { HttpVersion.Version20, false } }; - [ConditionalFact] + [Fact] public async Task AltSvc_ConnectionFrame_UpgradeFrom20_Success() { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/101376"); - } - using Http2LoopbackServer firstServer = Http2LoopbackServer.CreateServer(); using Http3LoopbackServer secondServer = CreateHttp3LoopbackServer(); using HttpClient client = CreateHttpClient(HttpVersion.Version20); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index cbd74f8188a20..48d72d4888013 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -13,8 +13,6 @@ using Xunit; using Xunit.Abstractions; -using Microsoft.DotNet.XUnitExtensions; - namespace System.Net.Http.Functional.Tests { using Configuration = System.Net.Test.Common.Configuration; @@ -289,17 +287,12 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }); } - [ConditionalTheory] + [Theory] [InlineData("Thu, 01 Dec 1994 16:00:00 GMT", true)] [InlineData("-1", false)] [InlineData("0", false)] public async Task SendAsync_Expires_Success(string value, bool isValid) { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); - } - await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using (HttpClient client = CreateHttpClient()) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index c7456f013b4bd..bdf38ef6e7fb6 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -854,20 +854,20 @@ public void DefaultProxy_Get_ReturnsNotNull() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DefaultProxy_SetGet_Roundtrips() + public async Task DefaultProxy_SetGet_Roundtrips() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy proxy = new WebProxy("http://localhost:3128/"); HttpClient.DefaultProxy = proxy; Assert.True(Object.ReferenceEquals(proxy, HttpClient.DefaultProxy)); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DefaultProxy_Credentials_SetGet_Roundtrips() + public async Task DefaultProxy_Credentials_SetGet_Roundtrips() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy proxy = HttpClient.DefaultProxy; ICredentials nc = proxy.Credentials; @@ -879,7 +879,7 @@ public void DefaultProxy_Credentials_SetGet_Roundtrips() Assert.Same(nc, proxy.Credentials); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }).DisposeAsync(); } [Fact] @@ -1371,7 +1371,7 @@ public async Task DefaultRequestVersion_UsedInCreatedMessages() var handler = new StoreMessageHttpMessageInvoker(); using (var client = new HttpClient(handler)) { - var version = new Version(1, 2, 3, 4); + var version = new Version(1, 1); client.DefaultRequestVersion = version; await client.GetAsync("http://doesntmatter", HttpCompletionOption.ResponseHeadersRead); Assert.Same(version, handler.Message.Version); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs index 74b7a1c5b0685..d7476a8bc6c6e 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs @@ -364,13 +364,13 @@ public Task RequestDuration_CustomTags_Recorded() [InlineData("System.Net.Http.HttpRequestOut.Start")] [InlineData("System.Net.Http.Request")] [InlineData("System.Net.Http.HttpRequestOut.Stop")] - public void RequestDuration_CustomTags_DiagnosticListener_Recorded(string eventName) + public async Task RequestDuration_CustomTags_DiagnosticListener_Recorded(string eventName) { - RemoteExecutor.Invoke(static async (testClassName, eventNameInner) => + await RemoteExecutor.Invoke(static async (testClassName, eventNameInner) => { using HttpMetricsTest test = (HttpMetricsTest)Activator.CreateInstance(Type.GetType(testClassName), (ITestOutputHelper)null); await test.RequestDuration_CustomTags_DiagnosticListener_Recorded_Core(eventNameInner); - }, GetType().FullName, eventName).Dispose(); + }, GetType().FullName, eventName).DisposeAsync(); } private async Task RequestDuration_CustomTags_DiagnosticListener_Recorded_Core(string eventName) @@ -1051,9 +1051,9 @@ public HttpMetricsTest_DefaultMeter(ITestOutputHelper output) : base(output) } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ActiveRequests_Success_Recorded() + public async Task ActiveRequests_Success_Recorded() { - RemoteExecutor.Invoke(static async Task () => + await RemoteExecutor.Invoke(static async Task () => { using HttpMetricsTest_DefaultMeter test = new(null); await test.LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -1072,7 +1072,7 @@ await test.LoopbackServerFactory.CreateClientAndServerAsync(async uri => { await server.AcceptConnectionSendResponseAndCloseAsync(); }); - }).Dispose(); + }).DisposeAsync(); } public static bool RemoteExecutorAndSocketsHttpHandlerSupported => RemoteExecutor.IsSupported && SocketsHttpHandler.IsSupported; @@ -1130,9 +1130,9 @@ await server.AcceptConnectionAsync(async connection => } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void RequestDuration_Success_Recorded() + public async Task RequestDuration_Success_Recorded() { - RemoteExecutor.Invoke(static async Task () => + await RemoteExecutor.Invoke(static async Task () => { using HttpMetricsTest_DefaultMeter test = new(null); await test.LoopbackServerFactory.CreateClientAndServerAsync(async uri => @@ -1148,7 +1148,7 @@ await test.LoopbackServerFactory.CreateClientAndServerAsync(async uri => { await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.OK); }); - }).Dispose(); + }).DisposeAsync(); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index 76d7086c37c17..ae1669c18e68b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -305,7 +305,7 @@ public async Task CancelPendingRequest_DropsStalledConnectionAttempt() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void CancelPendingRequest_DropsStalledConnectionAttempt_CustomPendingConnectionTimeout() + public async Task CancelPendingRequest_DropsStalledConnectionAttempt_CustomPendingConnectionTimeout() { if (UseVersion == HttpVersion.Version30) { @@ -316,7 +316,7 @@ public void CancelPendingRequest_DropsStalledConnectionAttempt_CustomPendingConn RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_PENDINGCONNECTIONTIMEOUTONREQUESTCOMPLETION"] = "42"; - RemoteExecutor.Invoke(CancelPendingRequest_DropsStalledConnectionAttempt_Impl, UseVersion.ToString(), options).Dispose(); + await RemoteExecutor.Invoke(CancelPendingRequest_DropsStalledConnectionAttempt_Impl, UseVersion.ToString(), options).DisposeAsync(); } private static async Task CancelPendingRequest_DropsStalledConnectionAttempt_Impl(string versionString) @@ -347,7 +347,7 @@ private static async Task CancelPendingRequest_DropsStalledConnectionAttempt_Imp [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(20_000)] [InlineData(Timeout.Infinite)] - public void PendingConnectionTimeout_HighValue_PendingConnectionIsNotCancelled(int timeout) + public async Task PendingConnectionTimeout_HighValue_PendingConnectionIsNotCancelled(int timeout) { if (UseVersion == HttpVersion.Version30) { @@ -355,7 +355,7 @@ public void PendingConnectionTimeout_HighValue_PendingConnectionIsNotCancelled(i return; } - RemoteExecutor.Invoke(static async (versionString, timoutStr) => + await RemoteExecutor.Invoke(static async (versionString, timoutStr) => { // Setup "infinite" timeout of int.MaxValue milliseconds AppContext.SetData("System.Net.SocketsHttpHandler.PendingConnectionTimeoutOnRequestCompletion", int.Parse(timoutStr)); @@ -390,7 +390,7 @@ public void PendingConnectionTimeout_HighValue_PendingConnectionIsNotCancelled(i await Assert.ThrowsAnyAsync(() => client.GetAsync("https://dummy", requestCts.Token)).WaitAsync(TestHelper.PassingTestTimeout); await connectionTestTcs.Task.WaitAsync(TestHelper.PassingTestTimeout); - }, UseVersion.ToString(), timeout.ToString()).Dispose(); + }, UseVersion.ToString(), timeout.ToString()).DisposeAsync(); } private sealed class SetTcsContent : StreamContent diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs index f1dc09a9f7358..72382374cfc6e 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs @@ -98,7 +98,7 @@ public async Task HighBandwidthDelayProduct_ClientStreamReceiveWindowWindowScale [OuterLoop("Runs long")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DisableDynamicWindowScaling_HighBandwidthDelayProduct_WindowRemainsConstant() + public async Task DisableDynamicWindowScaling_HighBandwidthDelayProduct_WindowRemainsConstant() { static async Task RunTest() { @@ -113,12 +113,12 @@ static async Task RunTest() Assert.Equal(DefaultInitialWindowSize, maxCredit); } - RemoteExecutor.Invoke(RunTest).Dispose(); + await RemoteExecutor.Invoke(RunTest).DisposeAsync(); } [OuterLoop("Runs long")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void MaxStreamWindowSize_WhenSet_WindowDoesNotScaleAboveMaximum() + public async Task MaxStreamWindowSize_WhenSet_WindowDoesNotScaleAboveMaximum() { const int MaxWindow = 654321; @@ -136,12 +136,12 @@ static async Task RunTest() RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_MAXSTREAMWINDOWSIZE"] = MaxWindow.ToString(); - RemoteExecutor.Invoke(RunTest, options).Dispose(); + await RemoteExecutor.Invoke(RunTest, options).DisposeAsync(); } [OuterLoop("Runs long")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void StreamWindowScaleThresholdMultiplier_HighValue_WindowScalesSlower() + public async Task StreamWindowScaleThresholdMultiplier_HighValue_WindowScalesSlower() { static async Task RunTest() { @@ -157,12 +157,12 @@ static async Task RunTest() RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_STREAMWINDOWSCALETHRESHOLDMULTIPLIER"] = "10000"; // Extreme value - RemoteExecutor.Invoke(RunTest, options).Dispose(); + await RemoteExecutor.Invoke(RunTest, options).DisposeAsync(); } [OuterLoop("Runs long")] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void StreamWindowScaleThresholdMultiplier_LowValue_WindowScalesFaster() + public async Task StreamWindowScaleThresholdMultiplier_LowValue_WindowScalesFaster() { static async Task RunTest() { @@ -178,7 +178,7 @@ static async Task RunTest() RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_FLOWCONTROL_STREAMWINDOWSCALETHRESHOLDMULTIPLIER"] = "0.00001"; // Extreme value - RemoteExecutor.Invoke(RunTest, options).Dispose(); + await RemoteExecutor.Invoke(RunTest, options).DisposeAsync(); } [OuterLoop("Runs long")] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2KeepAlivePing.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2KeepAlivePing.cs index a9beb393918b5..0f1c1b8d58c32 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2KeepAlivePing.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2KeepAlivePing.cs @@ -197,6 +197,7 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri => // The request should fail due to the connection being torn down due to KeepAlivePingTimeout. HttpProtocolException pex = Assert.IsType(ex.InnerException); Assert.Equal(HttpRequestError.HttpProtocolError, pex.HttpRequestError); + Assert.Contains("KeepAlivePingDelay", pex.Message); // Let connection live until server finishes: await _serverFinished.Task; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index f91fc2dda1dd3..62594756490ec 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -278,16 +278,16 @@ public SocketsHttpHandler_HttpClientHandler_MaxConnectionsPerServer_Test(ITestOu [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(true)] [InlineData(false)] - public void AppContextSetData_SetDefaultMaxConnectionsPerServer(bool asInt) + public async Task AppContextSetData_SetDefaultMaxConnectionsPerServer(bool asInt) { - RemoteExecutor.Invoke(static (asInt) => + await RemoteExecutor.Invoke(static (asInt) => { const int testValue = 123; object data = asInt == Boolean.TrueString ? testValue : testValue.ToString(); AppContext.SetData("System.Net.SocketsHttpHandler.MaxConnectionsPerServer", data); var handler = new HttpClientHandler(); Assert.Equal(testValue, handler.MaxConnectionsPerServer); - }, asInt.ToString()).Dispose(); + }, asInt.ToString()).DisposeAsync(); } [OuterLoop("Incurs a small delay")] @@ -1633,7 +1633,6 @@ public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http2(ITest protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/91757")] [ActiveIssue("https://github.com/dotnet/runtime/issues/101015")] @@ -1991,9 +1990,9 @@ await Http2LoopbackServerFactory.CreateServerAsync(async (server, url) => [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(false)] [InlineData(true)] - public void ConnectionsPooledThenDisposed_NoUnobservedTaskExceptions(bool secure) + public async Task ConnectionsPooledThenDisposed_NoUnobservedTaskExceptions(bool secure) { - RemoteExecutor.Invoke(async (secureString, useVersionString) => + await RemoteExecutor.Invoke(async (secureString, useVersionString) => { var releaseServer = new TaskCompletionSource(); await LoopbackServer.CreateClientAndServerAsync(async uri => @@ -2026,7 +2025,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => await releaseServer.Task; }), new LoopbackServer.Options { UseSsl = bool.Parse(secureString) }); - }, secure.ToString(), UseVersion.ToString()).Dispose(); + }, secure.ToString(), UseVersion.ToString()).DisposeAsync(); } [OuterLoop] @@ -4028,7 +4027,6 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http2(ITestOutputH protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Http3 : HttpClientHandlerTest { @@ -4036,19 +4034,13 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Http3(ITestOutputHelper outp protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3 : HttpClientHandlerTest_Cookies { public SocketsHttpHandlerTest_Cookies_Http3(ITestOutputHelper output) : base(output) { } protected override Version UseVersion => HttpVersion.Version30; - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/91757")] - public override Task GetAsync_DefaultCoookieContainer_NoCookieSent() { return null!; } } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3 : HttpClientHandlerTest_Headers { @@ -4056,7 +4048,6 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3(ITestOutputHel protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3 : SocketsHttpHandler_Cancellation_Test { @@ -4064,7 +4055,6 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3(ITestOutputH protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3 : HttpClientHandler_AltSvc_Test { @@ -4072,7 +4062,6 @@ public SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3(ITestOutputHelper protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Finalization_Http3 : HttpClientHandler_Finalization_Test { @@ -4237,7 +4226,6 @@ public SocketsHttpHandler_RequestContentLengthMismatchTest_Http2(ITestOutputHelp protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http3 : SocketsHttpHandler_RequestContentLengthMismatchTest { @@ -4414,7 +4402,6 @@ public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelpe protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest { @@ -4543,7 +4530,6 @@ await Http11LoopbackServerFactory.Singleton.CreateClientAndServerAsync(async uri } } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpRequestErrorTest_Http30 : SocketsHttpHandler_HttpRequestErrorTest { 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 0256d1d53a451..bdcdf4ada7c92 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 @@ -113,6 +113,8 @@ Link="Common\System\Net\SslProtocolSupport.cs" /> + TestMethods_MemberData() [OuterLoop] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(TestMethods_MemberData))] - public void EventSource_SuccessfulRequest_LogsStartStop(string testMethod) + public async Task EventSource_SuccessfulRequest_LogsStartStop(string testMethod) { if (UseVersion.Major != 1 && !testMethod.EndsWith("Async")) { @@ -59,7 +59,7 @@ public void EventSource_SuccessfulRequest_LogsStartStop(string testMethod) return; } - RemoteExecutor.Invoke(static async (useVersionString, testMethod) => + await RemoteExecutor.Invoke(static async (useVersionString, testMethod) => { const int ResponseContentLength = 42; @@ -188,13 +188,13 @@ await server.AcceptConnectionAsync(async connection => count: 1); ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: false, versionMajor: version.Major); - }, UseVersion.ToString(), testMethod).Dispose(); + }, UseVersion.ToString(), testMethod).DisposeAsync(); } [OuterLoop] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(TestMethods_MemberData))] - public void EventSource_UnsuccessfulRequest_LogsStartFailedStop(string testMethod) + public async Task EventSource_UnsuccessfulRequest_LogsStartFailedStop(string testMethod) { if (UseVersion.Major != 1 && !testMethod.EndsWith("Async")) { @@ -202,7 +202,7 @@ public void EventSource_UnsuccessfulRequest_LogsStartFailedStop(string testMetho return; } - RemoteExecutor.Invoke(static async (useVersionString, testMethod) => + await RemoteExecutor.Invoke(static async (useVersionString, testMethod) => { Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); @@ -293,7 +293,7 @@ await server.AcceptConnectionAsync(async connection => ValidateConnectionEstablishedClosed(events, version, expectedUri); ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: true, versionMajor: version.Major); - }, UseVersion.ToString(), testMethod).Dispose(); + }, UseVersion.ToString(), testMethod).DisposeAsync(); } [OuterLoop] @@ -304,7 +304,7 @@ await server.AcceptConnectionAsync(async connection => [InlineData("SendChunkedAsync")] [InlineData("InvokerSend")] [InlineData("InvokerSendAsync")] - public void EventSource_SendingRequestContent_LogsRequestContentStartStop(string testMethod) + public async Task EventSource_SendingRequestContent_LogsRequestContentStartStop(string testMethod) { if (UseVersion.Major != 1 && !testMethod.EndsWith("Async")) { @@ -312,7 +312,7 @@ public void EventSource_SendingRequestContent_LogsRequestContentStartStop(string return; } - RemoteExecutor.Invoke(static async (useVersionString, testMethod) => + await RemoteExecutor.Invoke(static async (useVersionString, testMethod) => { const int RequestContentLength = 42; const int ResponseContentLength = 43; @@ -396,7 +396,7 @@ await server.AcceptConnectionAsync(async connection => count: 1); ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: false, versionMajor: version.Major); - }, UseVersion.ToString(), testMethod).Dispose(); + }, UseVersion.ToString(), testMethod).DisposeAsync(); } private static void ValidateStartFailedStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, Version version, bool shouldHaveFailures = false, int count = 1) @@ -665,9 +665,9 @@ private static void ValidateEventCounters(ConcurrentQueue<(EventWrittenEventArgs [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_ConnectionPoolAtMaxConnections_LogsRequestLeftQueue() + public async Task EventSource_ConnectionPoolAtMaxConnections_LogsRequestLeftQueue() { - RemoteExecutor.Invoke(static async (useVersionString) => + await RemoteExecutor.Invoke(static async (useVersionString) => { Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); @@ -774,14 +774,14 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( ValidateRequestResponseStartStopEvents(events, requestContentLength: null, responseContentLength: 0, count: 3); ValidateEventCounters(events, requestCount: 3, shouldHaveFailures: false, versionMajor: version.Major, requestLeftQueue: true); - }, UseVersion.ToString()).Dispose(); + }, UseVersion.ToString()).DisposeAsync(); } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_Redirect_LogsRedirect() + public async Task EventSource_Redirect_LogsRedirect() { - RemoteExecutor.Invoke(static async (string useVersionString) => + await RemoteExecutor.Invoke(static async (string useVersionString) => { Version version = Version.Parse(useVersionString); @@ -821,7 +821,7 @@ await GetFactoryForVersion(version).CreateServerAsync((originalServer, originalU Assert.Equal(1, redirectEvent.Payload.Count); Assert.Equal(expectedUri.ToString(), (string)redirectEvent.Payload[0]); Assert.Equal("redirectUri", redirectEvent.PayloadNames[0]); - }, UseVersion.ToString()).Dispose(); + }, UseVersion.ToString()).DisposeAsync(); } public static bool SupportsRemoteExecutorAndAlpn = RemoteExecutor.IsSupported && PlatformDetection.SupportsAlpn; @@ -903,9 +903,9 @@ public TelemetryTest_Http11(ITestOutputHelper output) : base(output) { } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_ParallelRequests_LogsNewConnectionIdForEachRequest() + public async Task EventSource_ParallelRequests_LogsNewConnectionIdForEachRequest() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { const int NumParallelRequests = 4; @@ -970,7 +970,7 @@ async Task HandleConnectionAsync(GenericLoopbackConnection connection) Assert.True(connectionIds.Remove(connectionId), $"RequestHeadersStart has logged an unexpected connectionId={connectionId}."); } Assert.Empty(connectionIds); - }).Dispose(); + }).DisposeAsync(); } } diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs index 8eaa167fef1a8..351220837429e 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs @@ -52,7 +52,7 @@ public StressServer(Configuration configuration) if (configuration.UseHttpSys && OperatingSystem.IsWindows()) { // Use http.sys. This requires additional manual configuration ahead of time; - // see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys?view=aspnetcore-2.2#configure-windows-server. + // see https://learn.microsoft.com/aspnet/core/fundamentals/servers/httpsys?view=aspnetcore-2.2#configure-windows-server. // In particular, you need to: // 1. Create a self-signed cert and install it into your local personal store, e.g. New-SelfSignedCertificate -DnsName "localhost" -CertStoreLocation "cert:\LocalMachine\My" // 2. Pre-register the URL prefix, e.g. netsh http add urlacl url=https://localhost:5001/ user=Users diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs index 595c07a18ad81..6d9d50acde4d3 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Net.Http.Headers; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -137,9 +138,9 @@ public void ToString_UseResponseDirectiveValues_AllSerializedCorrectly() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ToString_NegativeValues_UsesMinusSignRegardlessOfCurrentCulture() + public async Task ToString_NegativeValues_UsesMinusSignRegardlessOfCurrentCulture() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { var cacheControl = new CacheControlHeaderValue() { @@ -155,7 +156,7 @@ public void ToString_NegativeValues_UsesMinusSignRegardlessOfCurrentCulture() CultureInfo.CurrentCulture = ci; Assert.Equal("max-age=-1, s-maxage=-4, max-stale=-2, min-fresh=-3", cacheControl.ToString()); - }).Dispose(); + }).DisposeAsync(); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs index 727e4f58d6908..a263f95bb27d8 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net.Http; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; using Xunit.Abstractions; @@ -41,9 +42,9 @@ public HttpEnvironmentProxyTest(ITestOutputHelper output) } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void HttpProxy_EnvironmentProxy_Loaded() + public async Task HttpProxy_EnvironmentProxy_Loaded() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy p; @@ -123,7 +124,7 @@ public void HttpProxy_EnvironmentProxy_Loaded() Assert.True(u != null && u.Host == "1.1.1.3" && u.Port == 3003); u = p.GetProxy(fooWss); Assert.True(u != null && u.Host == "1.1.1.5" && u.Port == 3005); - }).Dispose(); + }).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -144,9 +145,9 @@ public void HttpProxy_EnvironmentProxy_Loaded() [InlineData("socks5://1.2.3.4:8888/foo", "1.2.3.4", "8888", null, null)] [InlineData("https://1.1.1.5:3005", "1.1.1.5", "3005", null, null)] [InlineData("https://1.1.1.5", "1.1.1.5", "443", null, null)] - public void HttpProxy_Uri_Parsing(string _input, string _host, string _port, string _user, string _password) + public async Task HttpProxy_Uri_Parsing(string _input, string _host, string _port, string _user, string _password) { - RemoteExecutor.Invoke((input, host, port, user, password) => + await RemoteExecutor.Invoke((input, host, port, user, password) => { // Remote exec does not allow to pass null at this moment. if (user == "null") @@ -178,13 +179,13 @@ public void HttpProxy_Uri_Parsing(string _input, string _host, string _port, str } return RemoteExecutor.SuccessExitCode; - }, _input, _host, _port, _user ?? "null", _password ?? "null").Dispose(); + }, _input, _host, _port, _user ?? "null", _password ?? "null").DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void HttpProxy_CredentialParsing_Basic() + public async Task HttpProxy_CredentialParsing_Basic() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy p; @@ -210,14 +211,14 @@ public void HttpProxy_CredentialParsing_Basic() // This should not match Proxy Uri Assert.Null(p.Credentials.GetCredential(fooHttp, "Basic")); Assert.Null(p.Credentials.GetCredential(null, null)); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void HttpProxy_CredentialParsing_DefaultCredentials() + public async Task HttpProxy_CredentialParsing_DefaultCredentials() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy p; @@ -231,13 +232,13 @@ public void HttpProxy_CredentialParsing_DefaultCredentials() Assert.True(HttpEnvironmentProxy.TryCreate(out p)); Assert.NotNull(p); Assert.Equal(CredentialCache.DefaultCredentials, p.Credentials.GetCredential(p.GetProxy(fooHttp), "")); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void HttpProxy_Exceptions_Match() + public async Task HttpProxy_Exceptions_Match() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy p; @@ -251,7 +252,7 @@ public void HttpProxy_Exceptions_Match() Assert.True(p.IsBypassed(new Uri("http://test.com"))); Assert.False(p.IsBypassed(new Uri("http://1test.com"))); Assert.True(p.IsBypassed(new Uri("http://www.test.com"))); - }).Dispose(); + }).DisposeAsync(); } public static IEnumerable HttpProxyNoProxyEnvVarMemberData() @@ -264,14 +265,14 @@ public static IEnumerable HttpProxyNoProxyEnvVarMemberData() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(HttpProxyNoProxyEnvVarMemberData))] - public void HttpProxy_TryCreate_CaseInsensitiveVariables(string proxyEnvVar, string noProxyEnvVar) + public async Task HttpProxy_TryCreate_CaseInsensitiveVariables(string proxyEnvVar, string noProxyEnvVar) { string proxy = "http://foo:PLACEHOLDER@1.1.1.1:3000"; var options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables.Add(proxyEnvVar, proxy); options.StartInfo.EnvironmentVariables.Add(noProxyEnvVar, ".test.com, foo.com"); - RemoteExecutor.Invoke((proxy) => + await RemoteExecutor.Invoke((proxy) => { var directUri = new Uri("http://test.com"); var thruProxyUri = new Uri("http://atest.com"); @@ -282,7 +283,7 @@ public void HttpProxy_TryCreate_CaseInsensitiveVariables(string proxyEnvVar, str Assert.True(p.IsBypassed(directUri)); Assert.False(p.IsBypassed(thruProxyUri)); Assert.Equal(new Uri(proxy), p.GetProxy(thruProxyUri)); - }, proxy, options).Dispose(); + }, proxy, options).DisposeAsync(); } public static IEnumerable HttpProxyCgiEnvVarMemberData() @@ -296,7 +297,7 @@ public static IEnumerable HttpProxyCgiEnvVarMemberData() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(HttpProxyCgiEnvVarMemberData))] - public void HttpProxy_TryCreateAndPossibleCgi_HttpProxyUpperCaseDisabledInCgi( + public async Task HttpProxy_TryCreateAndPossibleCgi_HttpProxyUpperCaseDisabledInCgi( string proxyEnvVar, bool cgi, bool expectedProxyUse) { string proxy = "http://foo:PLACEHOLDER@1.1.1.1:3000"; @@ -308,7 +309,7 @@ public void HttpProxy_TryCreateAndPossibleCgi_HttpProxyUpperCaseDisabledInCgi( options.StartInfo.EnvironmentVariables.Add("GATEWAY_INTERFACE", "CGI/1.1"); } - RemoteExecutor.Invoke((proxy, expectedProxyUseString) => + await RemoteExecutor.Invoke((proxy, expectedProxyUseString) => { bool expectedProxyUse = bool.Parse(expectedProxyUseString); var destinationUri = new Uri("http://test.com"); @@ -324,7 +325,7 @@ public void HttpProxy_TryCreateAndPossibleCgi_HttpProxyUpperCaseDisabledInCgi( { Assert.False(created); } - }, proxy, expectedProxyUse.ToString(), options).Dispose(); + }, proxy, expectedProxyUse.ToString(), options).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs index 50357999b8762..df6648aab2730 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Net.Http; using System.Net.Http.WinHttpHandlerUnitTests; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; using Xunit.Abstractions; @@ -32,9 +33,9 @@ public HttpWindowsProxyTest(ITestOutputHelper output) [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(ProxyParsingData))] - public void HttpProxy_WindowsProxy_Manual_Loaded(string rawProxyString, string rawInsecureUri, string rawSecureUri) + public async Task HttpProxy_WindowsProxy_Manual_Loaded(string rawProxyString, string rawInsecureUri, string rawSecureUri) { - RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => + await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { FakeRegistry.Reset(); @@ -54,14 +55,14 @@ public void HttpProxy_WindowsProxy_Manual_Loaded(string rawProxyString, string r Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooHttps))); Assert.Equal(!string.IsNullOrEmpty(insecureProxy) ? new Uri(insecureProxy) : null, p.GetProxy(new Uri(fooWs))); Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooWss))); - }, rawProxyString, rawInsecureUri ?? string.Empty, rawSecureUri ?? string.Empty).Dispose(); + }, rawProxyString, rawInsecureUri ?? string.Empty, rawSecureUri ?? string.Empty).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(ProxyParsingData))] - public void HttpProxy_WindowsProxy_PAC_Loaded(string rawProxyString, string rawInsecureUri, string rawSecureUri) + public async Task HttpProxy_WindowsProxy_PAC_Loaded(string rawProxyString, string rawInsecureUri, string rawSecureUri) { - RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => + await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { TestControl.ResetAll(); @@ -87,7 +88,7 @@ public void HttpProxy_WindowsProxy_PAC_Loaded(string rawProxyString, string rawI Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooHttps))); Assert.Equal(!string.IsNullOrEmpty(insecureProxy) ? new Uri(insecureProxy) : null, p.GetProxy(new Uri(fooWs))); Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooWss))); - }, rawProxyString, rawInsecureUri ?? string.Empty, rawSecureUri ?? string.Empty).Dispose(); + }, rawProxyString, rawInsecureUri ?? string.Empty, rawSecureUri ?? string.Empty).DisposeAsync(); } public static TheoryData ProxyParsingData => @@ -117,9 +118,9 @@ public void HttpProxy_WindowsProxy_PAC_Loaded(string rawProxyString, string rawI [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData("localhost:1234", "http://localhost:1234/")] [InlineData("123.123.123.123", "http://123.123.123.123/")] - public void HttpProxy_WindowsProxy_Loaded(string rawProxyString, string expectedUri) + public async Task HttpProxy_WindowsProxy_Loaded(string rawProxyString, string expectedUri) { - RemoteExecutor.Invoke((proxyString, expectedString) => + await RemoteExecutor.Invoke((proxyString, expectedString) => { IWebProxy p; @@ -132,7 +133,7 @@ public void HttpProxy_WindowsProxy_Loaded(string rawProxyString, string expected Assert.NotNull(p); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttp)).ToString()); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttps)).ToString()); - }, rawProxyString, expectedUri).Dispose(); + }, rawProxyString, expectedUri).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -153,9 +154,9 @@ public void HttpProxy_WindowsProxy_Loaded(string rawProxyString, string expected [InlineData("http://[2607:f8B0:4005:80A::200E]/", true)] [InlineData("http://b\u00e9b\u00e9.eu/", true)] [InlineData("http://www.b\u00e9b\u00e9.eu/", true)] - public void HttpProxy_Local_Bypassed(string name, bool shouldBypass) + public async Task HttpProxy_Local_Bypassed(string name, bool shouldBypass) { - RemoteExecutor.Invoke((url, expected) => + await RemoteExecutor.Invoke((url, expected) => { bool expectedResult = Boolean.Parse(expected); IWebProxy p; @@ -169,7 +170,7 @@ public void HttpProxy_Local_Bypassed(string name, bool shouldBypass) Uri u = new Uri(url); Assert.Equal(expectedResult, p.GetProxy(u) == null); - }, name, shouldBypass.ToString()).Dispose(); + }, name, shouldBypass.ToString()).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -178,9 +179,9 @@ public void HttpProxy_Local_Bypassed(string name, bool shouldBypass) [InlineData(" ; ; ", 0)] [InlineData("http://127.0.0.1/", 1)] [InlineData("[::]", 1)] - public void HttpProxy_Local_Parsing(string bypass, int count) + public async Task HttpProxy_Local_Parsing(string bypass, int count) { - RemoteExecutor.Invoke((bypassValue, expected) => + await RemoteExecutor.Invoke((bypassValue, expected) => { int expectedCount = Convert.ToInt32(expected); IWebProxy p; @@ -203,7 +204,7 @@ public void HttpProxy_Local_Parsing(string bypass, int count) { Assert.Null(sp.BypassList); } - }, bypass, count.ToString()).Dispose(); + }, bypass, count.ToString()).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -212,9 +213,9 @@ public void HttpProxy_Local_Parsing(string bypass, int count) [InlineData("http://;")] [InlineData("http=;")] [InlineData(" ; ")] - public void HttpProxy_InvalidWindowsProxy_Null(string rawProxyString) + public async Task HttpProxy_InvalidWindowsProxy_Null(string rawProxyString) { - RemoteExecutor.Invoke((proxyString) => + await RemoteExecutor.Invoke((proxyString) => { IWebProxy p; @@ -231,14 +232,14 @@ public void HttpProxy_InvalidWindowsProxy_Null(string rawProxyString) Assert.Null(p.GetProxy(new Uri(fooHttps))); Assert.Null(p.GetProxy(new Uri(fooWs))); Assert.Null(p.GetProxy(new Uri(fooWss))); - }, rawProxyString).Dispose(); + }, rawProxyString).DisposeAsync(); } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(HttpProxy_Multi_Data))] - public void HttpProxy_Multi_Success(bool manualConfig, string proxyConfig, string url, string expected) + public async Task HttpProxy_Multi_Success(bool manualConfig, string proxyConfig, string url, string expected) { - RemoteExecutor.Invoke((manualConfigValue, proxyConfigValue, urlValue, expectedValue) => + await RemoteExecutor.Invoke((manualConfigValue, proxyConfigValue, urlValue, expectedValue) => { bool manual = bool.Parse(manualConfigValue); Uri requestUri = new Uri(urlValue); @@ -276,7 +277,7 @@ public void HttpProxy_Multi_Success(bool manualConfig, string proxyConfig, strin } Assert.False(multi.ReadNext(out _, out _)); - }, manualConfig.ToString(), proxyConfig, url, expected).Dispose(); + }, manualConfig.ToString(), proxyConfig, url, expected).DisposeAsync(); } public static IEnumerable HttpProxy_Multi_Data() @@ -294,11 +295,11 @@ public static IEnumerable HttpProxy_Multi_Data() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(false)] [InlineData(true)] - public void HttpProxy_Multi_ConcurrentUse_Success(bool manualConfig) + public async Task HttpProxy_Multi_ConcurrentUse_Success(bool manualConfig) { const string MultiProxyConfig = "http://proxy-a.com http://proxy-b.com http://proxy-c.com"; - RemoteExecutor.Invoke(manualValue => + await RemoteExecutor.Invoke(manualValue => { bool manual = bool.Parse(manualValue); @@ -364,7 +365,7 @@ public void HttpProxy_Multi_ConcurrentUse_Success(bool manualConfig) Assert.True(multiC.ReadNext(out Uri proxyC, out _)); Assert.Equal(firstProxy, proxyC); Assert.False(multiC.ReadNext(out proxyC, out _)); - }, manualConfig.ToString()).Dispose(); + }, manualConfig.ToString()).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index e8a6d2776a0dd..05f9963958ced 100755 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -51,6 +51,8 @@ Link="Common\System\Net\Http\HPackEncoder.cs" /> + + await RemoteExecutor.Invoke(() => { IWebProxy proxy = SystemProxyInfo.ConstructSystemProxy(); Assert.NotNull(proxy); HttpEnvironmentProxy envProxy = proxy as HttpEnvironmentProxy; Assert.Null(envProxy); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void Ctor_ProxyEnvironmentVariableSet_IsHttpEnvironmentProxy() + public async Task Ctor_ProxyEnvironmentVariableSet_IsHttpEnvironmentProxy() { var options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables.Add("http_proxy", "http://proxy.contoso.com"); - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { IWebProxy proxy = SystemProxyInfo.ConstructSystemProxy(); HttpEnvironmentProxy envProxy = proxy as HttpEnvironmentProxy; Assert.NotNull(envProxy); - }, options).Dispose(); + }, options).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerTimeoutManager.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerTimeoutManager.Windows.cs index 5b6636bac935e..222b3b51169e3 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerTimeoutManager.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerTimeoutManager.Windows.cs @@ -8,7 +8,7 @@ namespace System.Net { // // See the native HTTP_TIMEOUT_LIMIT_INFO structure documentation for additional information. - // https://docs.microsoft.com/en-us/windows/desktop/api/http/ns-http-_http_timeout_limit_info + // https://learn.microsoft.com/windows/desktop/api/http/ns-http-_http_timeout_limit_info // public class HttpListenerTimeoutManager { diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs b/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs index c4c9e801fd78c..188dd1711f52f 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/LoggingTest.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.Tracing; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -25,9 +26,9 @@ public void EventSource_ExistsWithCorrectId() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_EventsRaisedAsExpected() + public async Task EventSource_EventsRaisedAsExpected() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.Mail", EventLevel.Verbose)) { @@ -40,7 +41,7 @@ public void EventSource_EventsRaisedAsExpected() Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself Assert.InRange(events.Count, 1, int.MaxValue); } - }).Dispose(); + }).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj b/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj index 6ca3cb08be0d9..7336f9199077a 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj +++ b/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj @@ -27,6 +27,8 @@ Link="Common\System\IO\TempFile.cs" /> + diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs index b968b6a0ab147..920a5af60cf00 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs @@ -30,11 +30,11 @@ public static void EventSource_ExistsWithCorrectId() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void GetHostEntry_InvalidHost_LogsError() + public async Task GetHostEntry_InvalidHost_LogsError() { try { - RemoteExecutor.Invoke(static () => + await RemoteExecutor.Invoke(static () => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.NameResolution", EventLevel.Error)) { @@ -65,7 +65,7 @@ public void GetHostEntry_InvalidHost_LogsError() Assert.NotNull(ev.Payload[2]); } } - }).Dispose(); + }).DisposeAsync(); } catch (Exception ex) when (ex.ToString().Contains(nameof(SkipTestException), StringComparison.Ordinal)) { @@ -74,11 +74,11 @@ public void GetHostEntry_InvalidHost_LogsError() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void GetHostEntryAsync_InvalidHost_LogsError() + public async Task GetHostEntryAsync_InvalidHost_LogsError() { try { - RemoteExecutor.Invoke(static async () => + await RemoteExecutor.Invoke(static async () => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.NameResolution", EventLevel.Error)) { @@ -110,7 +110,7 @@ await listener.RunWithCallbackAsync(ev => events.Enqueue(ev), async () => Assert.NotNull(ev.Payload[2]); } } - }).Dispose(); + }).DisposeAsync(); } catch (Exception ex) when (ex.ToString().Contains(nameof(SkipTestException), StringComparison.Ordinal)) { @@ -132,11 +132,11 @@ static async Task WaitForErrorEventAsync(ConcurrentQueue } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void GetHostEntry_ValidName_NoErrors() + public async Task GetHostEntry_ValidName_NoErrors() { try { - RemoteExecutor.Invoke(static () => + await RemoteExecutor.Invoke(static () => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.NameResolution", EventLevel.Verbose)) { @@ -162,7 +162,7 @@ public void GetHostEntry_ValidName_NoErrors() // No errors or warning for successful query. Assert.True(events.Count(ev => (int)ev.Level > (int)EventLevel.Informational) == 0); } - }).Dispose(); + }).DisposeAsync(); } catch (Exception ex) when (ex.ToString().Contains(nameof(SkipTestException), StringComparison.Ordinal)) { diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/MetricsTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/MetricsTest.cs index a19d9edc476ab..e72a7f2940a13 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/MetricsTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/MetricsTest.cs @@ -17,9 +17,9 @@ public class MetricsTest private const string DnsLookupDuration = "dns.lookup.duration"; [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void ResolveValidHostName_MetricsRecorded() + public static async Task ResolveValidHostName_MetricsRecorded() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { const string ValidHostName = "localhost"; @@ -38,7 +38,7 @@ public static void ResolveValidHostName_MetricsRecorded() Assert.Equal(6, measurements.Length); Assert.All(measurements, m => Assert.True(m > double.Epsilon)); - }).Dispose(); + }).DisposeAsync(); } [Fact] diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj index 402868113d600..ff009e7b84326 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj @@ -24,6 +24,8 @@ Link="Common\System\Net\Configuration.cs" /> + + await RemoteExecutor.Invoke(async () => { const string ValidHostName = "microsoft.com"; @@ -51,14 +51,14 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn }); VerifyEvents(events, ValidHostName, 6); - }).Dispose(); + }).DisposeAsync(); } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void EventSource_ResolveInvalidHostName_LogsStartFailureStop() + public static async Task EventSource_ResolveInvalidHostName_LogsStartFailureStop() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { const string InvalidHostName = "invalid...example.com"; @@ -79,14 +79,14 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn }); VerifyEvents(events, InvalidHostName, 6, shouldHaveFailures: true); - }).Dispose(); + }).DisposeAsync(); } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void EventSource_GetHostEntryForIP_LogsStartStop() + public static async Task EventSource_GetHostEntryForIP_LogsStartStop() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { const string ValidIPAddress = "8.8.4.4"; @@ -110,7 +110,7 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn // Each GetHostEntry over an IP will yield 2 resolutions VerifyEvents(events, ValidIPAddress, 12, isHostEntryForIp: true); - }).Dispose(); + }).DisposeAsync(); } private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string hostname, int expectedNumber, bool shouldHaveFailures = false, bool isHostEntryForIp = false) @@ -149,7 +149,7 @@ private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, G } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void ResolutionsWaitingOnQueue_ResolutionStartCalledBeforeEnqueued() + public static async Task ResolutionsWaitingOnQueue_ResolutionStartCalledBeforeEnqueued() { // Some platforms (non-Windows) don't have proper support for GetAddrInfoAsync. // Instead we perform async-over-sync with a per-host queue. @@ -157,7 +157,7 @@ public static void ResolutionsWaitingOnQueue_ResolutionStartCalledBeforeEnqueued // We do this by blocking the first ResolutionStart event. // If the event was logged after waiting on the queue, the second request would never complete. - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { using var listener = new TestEventListener("System.Net.NameResolution", EventLevel.Informational); listener.AddActivityTracking(); @@ -221,7 +221,7 @@ static Task DoResolutionAsync() Assert.NotEqual(events[0].ActivityId, events[1].ActivityId); Assert.False(callbackWaitTimedOut); - }).Dispose(); + }).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs index 8a99feaeec369..2f2ea81ab468a 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs @@ -85,6 +85,8 @@ internal static unsafe NetworkInterface[] GetNetworkInterfaces() List interfaceList = new List(); Interop.IpHlpApi.GetAdaptersAddressesFlags flags = + Interop.IpHlpApi.GetAdaptersAddressesFlags.IncludeGateways | + Interop.IpHlpApi.GetAdaptersAddressesFlags.IncludeWins | Interop.IpHlpApi.GetAdaptersAddressesFlags.IncludeAllInterfaces; // Figure out the right buffer size for the adapter information. diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs index 0b98a87e80cfa..f21cbce950a54 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs @@ -832,7 +832,7 @@ public void SendPingWithIPAddressAndTimeoutAndBufferAndPingOptions_ElevatedUnix( [InlineData(AddressFamily.InterNetworkV6, "ja_JP.UTF8", null, null)] [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", "ja_JP.UTF8", null)] [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", null, "ja_JP.UTF8")] - public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) + public async Task SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(addressFamily); if (localIpAddress == null) @@ -847,7 +847,7 @@ public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, str remoteInvokeStartInfo.EnvironmentVariables["LC_MESSAGES"] = envVar_LC_MESSAGES; remoteInvokeStartInfo.EnvironmentVariables["LC_ALL"] = envVar_LC_ALL; - RemoteExecutor.Invoke(address => + await RemoteExecutor.Invoke(address => { SendBatchPing( (ping) => ping.Send(address, TestSettings.PingTimeout), @@ -855,7 +855,7 @@ public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, str { PingResultValidator(pingReply, new IPAddress[] { IPAddress.Parse(address) }, null); }); - }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).Dispose(); + }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).DisposeAsync(); } [PlatformSpecific(TestPlatforms.AnyUnix)] @@ -866,7 +866,7 @@ public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, str [InlineData(AddressFamily.InterNetworkV6, "ja_JP.UTF8", null, null)] [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", "ja_JP.UTF8", null)] [InlineData(AddressFamily.InterNetworkV6, "en_US.UTF8", null, "ja_JP.UTF8")] - public void SendPingAsync_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) + public async Task SendPingAsync_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(addressFamily); @@ -879,7 +879,7 @@ public void SendPingAsync_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily } }; - RemoteExecutor.Invoke(async address => + await RemoteExecutor.Invoke(async address => { await SendBatchPingAsync( (ping) => ping.SendPingAsync(address), @@ -887,7 +887,7 @@ await SendBatchPingAsync( { PingResultValidator(pingReply, new IPAddress[] { IPAddress.Parse(address) }, null); }); - }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).Dispose(); + }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).DisposeAsync(); } [ConditionalFact(nameof(UsesPingUtility))] diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj index bdacb51543d8c..8ac93b90759cf 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/System.Net.Ping.Functional.Tests.csproj @@ -18,6 +18,8 @@ + + await RemoteExecutor.Invoke(() => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.Primitives", EventLevel.Verbose)) { @@ -40,7 +41,7 @@ public void EventSource_EventsRaisedAsExpected() Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself Assert.InRange(events.Count, 1, int.MaxValue); } - }).Dispose(); + }).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj b/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj index 86fc2553bd379..89ce40cdd2b1f 100644 --- a/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj +++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj @@ -25,7 +25,10 @@ - + + \ No newline at end of file diff --git a/src/libraries/System.Net.Quic/readme.md b/src/libraries/System.Net.Quic/readme.md index 039ce733a233a..e8ba3e7f32c6a 100644 --- a/src/libraries/System.Net.Quic/readme.md +++ b/src/libraries/System.Net.Quic/readme.md @@ -18,7 +18,7 @@ During the build, the `msquic.dll` is automatically downloaded and placed in cor ### Linux -On Linux, `libmsquic` is published via official Microsoft Linux package repository `packages.microsoft.com`. In order to consume packages from it, you have to add it manually, see https://docs.microsoft.com/en-us/windows-server/administration/linux-package-repository-for-microsoft-software. After that, you should be able to install `libmsquic` via the package manager of your distro, e.g. for Ubuntu: +On Linux, `libmsquic` is published via official Microsoft Linux package repository `packages.microsoft.com`. In order to consume packages from it, you have to add it manually, see https://learn.microsoft.com/windows-server/administration/linux-package-repository-for-microsoft-software. After that, you should be able to install `libmsquic` via the package manager of your distro, e.g. for Ubuntu: ``` apt install libmsquic ``` diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicRemoteExecutorTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicRemoteExecutorTests.cs index d2a40a1f35af5..68e6a88403df8 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicRemoteExecutorTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicRemoteExecutorTests.cs @@ -23,7 +23,7 @@ public MsQuicRemoteExecutorTests() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(true)] [InlineData(false)] - public void SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) + public async Task SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) { if (PlatformDetection.IsDebugLibrary(typeof(QuicConnection).Assembly) && !enabledBySwitch) { @@ -36,7 +36,7 @@ public void SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) var tempFile = Path.GetTempFileName(); psi.Environment.Add("SSLKEYLOGFILE", tempFile); - RemoteExecutor.Invoke(async (enabledBySwitch) => + await RemoteExecutor.Invoke(async (enabledBySwitch) => { if (bool.Parse(enabledBySwitch)) { @@ -47,7 +47,7 @@ public void SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) await clientConnection.DisposeAsync(); await serverConnection.DisposeAsync(); } - , enabledBySwitch.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + , enabledBySwitch.ToString(), new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync(); if (enabledBySwitch) { diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs index f8dd160acb00b..5125c72e0ca8d 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs @@ -15,7 +15,7 @@ namespace System.Net.Quic.Tests; -[CollectionDefinition(nameof(QuicTestCollection), DisableParallelization = true)] +[CollectionDefinition(nameof(QuicTestCollection))] public unsafe class QuicTestCollection : ICollectionFixture, IDisposable { public static bool IsSupported => QuicListener.IsSupported && QuicConnection.IsSupported; diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index 67faecde3b4c9..c237c7d942a95 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -25,6 +25,7 @@ + diff --git a/src/libraries/System.Net.Requests/tests/AuthenticationManagerTest.cs b/src/libraries/System.Net.Requests/tests/AuthenticationManagerTest.cs index f5e7654c255dc..51fe3c15be4bb 100644 --- a/src/libraries/System.Net.Requests/tests/AuthenticationManagerTest.cs +++ b/src/libraries/System.Net.Requests/tests/AuthenticationManagerTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -33,29 +34,29 @@ public void Unregister_Null_Throws() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void Register_Unregister_ModuleCountUnchanged() + public async Task Register_Unregister_ModuleCountUnchanged() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { int initialCount = GetModuleCount(); IAuthenticationModule module = new CustomModule(); AuthenticationManager.Register(module); AuthenticationManager.Unregister(module); Assert.Equal(initialCount, GetModuleCount()); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void Register_UnregisterByScheme_ModuleCountUnchanged() + public async Task Register_UnregisterByScheme_ModuleCountUnchanged() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { int initialCount = GetModuleCount(); IAuthenticationModule module = new CustomModule(); AuthenticationManager.Register(module); AuthenticationManager.Unregister("custom"); Assert.Equal(initialCount, GetModuleCount()); - }).Dispose(); + }).DisposeAsync(); } [Fact] @@ -68,11 +69,11 @@ public void RegisteredModules_DefaultCount_ExpectedValue() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void CredentialPolicy_Roundtrip() + public async Task CredentialPolicy_Roundtrip() { Assert.Null(AuthenticationManager.CredentialPolicy); - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { ICredentialPolicy cp = new DummyCredentialPolicy(); AuthenticationManager.CredentialPolicy = cp; @@ -80,17 +81,17 @@ public void CredentialPolicy_Roundtrip() AuthenticationManager.CredentialPolicy = null; Assert.Null(AuthenticationManager.CredentialPolicy); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void CustomTargetNameDictionary_ValidCollection() + public async Task CustomTargetNameDictionary_ValidCollection() { Assert.NotNull(AuthenticationManager.CustomTargetNameDictionary); Assert.Empty(AuthenticationManager.CustomTargetNameDictionary); Assert.Same(AuthenticationManager.CustomTargetNameDictionary, AuthenticationManager.CustomTargetNameDictionary); - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { string theKey = "http://www.contoso.com"; string theValue = "HTTP/www.contoso.com"; @@ -99,7 +100,7 @@ public void CustomTargetNameDictionary_ValidCollection() AuthenticationManager.CustomTargetNameDictionary.Clear(); Assert.Equal(0, AuthenticationManager.CustomTargetNameDictionary.Count); - }).Dispose(); + }).DisposeAsync(); } private static int GetModuleCount() diff --git a/src/libraries/System.Net.Requests/tests/GlobalProxySelectionTest.cs b/src/libraries/System.Net.Requests/tests/GlobalProxySelectionTest.cs index fa3bfeb7ce3c9..7dfcf91f52e42 100644 --- a/src/libraries/System.Net.Requests/tests/GlobalProxySelectionTest.cs +++ b/src/libraries/System.Net.Requests/tests/GlobalProxySelectionTest.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.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -37,9 +38,9 @@ public bool IsBypassed(Uri host) } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void Select_Success() + public async Task Select_Success() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { var myProxy = new MyWebProxy(); @@ -82,7 +83,7 @@ public void Select_Success() Assert.NotNull(GlobalProxySelection.Select); Assert.True(GlobalProxySelection.Select.IsBypassed(null)); // This is true for EmptyWebProxy, but not for most proxies #pragma warning restore 0618 - }).Dispose(); + }).DisposeAsync(); } [Fact] diff --git a/src/libraries/System.Net.Requests/tests/HttpWebRequestTest.cs b/src/libraries/System.Net.Requests/tests/HttpWebRequestTest.cs index c6d693cdf5004..7a3077b03ecde 100644 --- a/src/libraries/System.Net.Requests/tests/HttpWebRequestTest.cs +++ b/src/libraries/System.Net.Requests/tests/HttpWebRequestTest.cs @@ -785,9 +785,9 @@ public void Expect_Set100Continue_ThrowsArgumentException(Uri remoteServer) } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DefaultMaximumResponseHeadersLength_SetAndGetLength_ValuesMatch() + public async Task DefaultMaximumResponseHeadersLength_SetAndGetLength_ValuesMatch() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { int defaultMaximumResponseHeadersLength = HttpWebRequest.DefaultMaximumResponseHeadersLength; const int NewDefaultMaximumResponseHeadersLength = 255; @@ -801,13 +801,13 @@ public void DefaultMaximumResponseHeadersLength_SetAndGetLength_ValuesMatch() { HttpWebRequest.DefaultMaximumResponseHeadersLength = defaultMaximumResponseHeadersLength; } - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DefaultMaximumErrorResponseLength_SetAndGetLength_ValuesMatch() + public async Task DefaultMaximumErrorResponseLength_SetAndGetLength_ValuesMatch() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { int defaultMaximumErrorsResponseLength = HttpWebRequest.DefaultMaximumErrorResponseLength; const int NewDefaultMaximumErrorsResponseLength = 255; @@ -821,13 +821,13 @@ public void DefaultMaximumErrorResponseLength_SetAndGetLength_ValuesMatch() { HttpWebRequest.DefaultMaximumErrorResponseLength = defaultMaximumErrorsResponseLength; } - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DefaultCachePolicy_SetAndGetPolicyReload_ValuesMatch() + public async Task DefaultCachePolicy_SetAndGetPolicyReload_ValuesMatch() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { RequestCachePolicy requestCachePolicy = HttpWebRequest.DefaultCachePolicy; @@ -841,7 +841,7 @@ public void DefaultCachePolicy_SetAndGetPolicyReload_ValuesMatch() { HttpWebRequest.DefaultCachePolicy = requestCachePolicy; } - }).Dispose(); + }).DisposeAsync(); } [Theory, MemberData(nameof(EchoServers))] @@ -1587,8 +1587,6 @@ public void Proxy_GetDefault_ExpectNotNull(Uri remoteServer) Assert.NotNull(request.Proxy); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/31380")] - [OuterLoop("Uses external servers")] [PlatformSpecific(TestPlatforms.AnyUnix)] // The default proxy is resolved via WinINet on Windows. [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public async Task ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed() @@ -1608,7 +1606,7 @@ await LoopbackServer.CreateServerAsync(async (proxyServer, proxyUri) => proxyTask = proxyServer.AcceptConnectionPerformAuthenticationAndCloseAsync("Proxy-Authenticate: Basic realm=\"NetCore\"\r\n"); psi.Environment.Add("http_proxy", $"http://{proxyUri.Host}:{proxyUri.Port}"); - RemoteExecutor.Invoke(async (async, user, pw) => + await RemoteExecutor.Invoke(async (async, user, pw) => { WebRequest.DefaultWebProxy.Credentials = new NetworkCredential(user, pw); HttpWebRequest request = HttpWebRequest.CreateHttp(Configuration.Http.RemoteEchoServer); @@ -1617,7 +1615,7 @@ await LoopbackServer.CreateServerAsync(async (proxyServer, proxyUri) => { Assert.Equal(HttpStatusCode.OK, response.StatusCode); } - }, (this is HttpWebRequestTest_Async).ToString(), cred.UserName, cred.Password, new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + }, (this is HttpWebRequestTest_Async).ToString(), cred.UserName, cred.Password, new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync(); await proxyTask; }, options); @@ -1724,9 +1722,9 @@ public void MediaType_SetThenGet_ValuesMatch(Uri remoteServer) } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported)), MemberData(nameof(MixedWebRequestParameters))] - public void GetResponseAsync_ParametersAreNotCachable_CreateNewClient(HttpWebRequestParameters requestParameters, bool connectionReusedParameter) + public async Task GetResponseAsync_ParametersAreNotCachable_CreateNewClient(HttpWebRequestParameters requestParameters, bool connectionReusedParameter) { - RemoteExecutor.Invoke(async (async, serializedParameters, connectionReusedString) => + await RemoteExecutor.Invoke(async (async, serializedParameters, connectionReusedString) => { var parameters = JsonSerializer.Deserialize(serializedParameters); @@ -1769,13 +1767,13 @@ public void GetResponseAsync_ParametersAreNotCachable_CreateNewClient(HttpWebReq } } } - }, (this is HttpWebRequestTest_Async).ToString(), JsonSerializer.Serialize(requestParameters), connectionReusedParameter.ToString()).Dispose(); + }, (this is HttpWebRequestTest_Async).ToString(), JsonSerializer.Serialize(requestParameters), connectionReusedParameter.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void GetResponseAsync_ParametersAreCachableButDifferent_CreateNewClient() + public async Task GetResponseAsync_ParametersAreCachableButDifferent_CreateNewClient() { - RemoteExecutor.Invoke(async (async) => + await RemoteExecutor.Invoke(async (async) => { using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { @@ -1829,7 +1827,7 @@ public void GetResponseAsync_ParametersAreCachableButDifferent_CreateNewClient() } } } - }, (this is HttpWebRequestTest_Async).ToString()).Dispose(); + }, (this is HttpWebRequestTest_Async).ToString()).DisposeAsync(); } [Fact] @@ -2000,10 +1998,10 @@ await server.AcceptConnectionAsync(async connection => [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(RequestCacheLevel.NoCacheNoStore, "Cache-Control: no-store, no-cache")] [InlineData(RequestCacheLevel.Reload, "Cache-Control: no-cache")] - public void SendHttpGetRequest_WithGlobalCachePolicy_AddCacheHeaders( + public async Task SendHttpGetRequest_WithGlobalCachePolicy_AddCacheHeaders( RequestCacheLevel requestCacheLevel, string expectedHeader) { - RemoteExecutor.Invoke(async (async, reqCacheLevel, eh) => + await RemoteExecutor.Invoke(async (async, reqCacheLevel, eh) => { await LoopbackServer.CreateServerAsync(async (server, uri) => { @@ -2023,7 +2021,7 @@ await server.AcceptConnectionAsync(async connection => Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }); - }, (this is HttpWebRequestTest_Async).ToString(), requestCacheLevel.ToString(), expectedHeader).Dispose(); + }, (this is HttpWebRequestTest_Async).ToString(), requestCacheLevel.ToString(), expectedHeader).DisposeAsync(); } [Theory] @@ -2040,9 +2038,9 @@ public async Task SendHttpGetRequest_WithCachePolicyCacheOnly_ThrowException( } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendHttpGetRequest_WithGlobalCachePolicyBypassCache_DoNotAddCacheHeaders() + public async Task SendHttpGetRequest_WithGlobalCachePolicyBypassCache_DoNotAddCacheHeaders() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { await LoopbackServer.CreateServerAsync(async (server, uri) => { @@ -2066,7 +2064,7 @@ await server.AcceptConnectionAsync(async connection => Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }); - }).Dispose(); + }).DisposeAsync(); } [Fact] @@ -2310,9 +2308,9 @@ await server.AcceptConnectionAsync( } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendHttpRequest_WhenDefaultMaximumErrorResponseLengthSet_Success() + public async Task SendHttpRequest_WhenDefaultMaximumErrorResponseLengthSet_Success() { - RemoteExecutor.Invoke(async (async) => + await RemoteExecutor.Invoke(async (async) => { TaskCompletionSource tcs = new TaskCompletionSource(); await LoopbackServer.CreateClientAndServerAsync( @@ -2342,7 +2340,7 @@ await server.AcceptConnectionAsync( await tcs.Task; }); }); - }, IsAsync.ToString()).Dispose(); + }, IsAsync.ToString()).DisposeAsync(); } [Fact] @@ -2358,9 +2356,9 @@ public void HttpWebRequest_SetProtocolVersion_Success() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendHttpRequest_BindIPEndPoint_Success() + public async Task SendHttpRequest_BindIPEndPoint_Success() { - RemoteExecutor.Invoke(async (async) => + await RemoteExecutor.Invoke(async (async) => { TaskCompletionSource tcs = new TaskCompletionSource(); await LoopbackServer.CreateClientAndServerAsync( @@ -2386,13 +2384,13 @@ await server.AcceptConnectionAsync( await tcs.Task; }); }); - }, IsAsync.ToString()).Dispose(); + }, IsAsync.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendHttpRequest_BindIPEndPoint_Throws() + public async Task SendHttpRequest_BindIPEndPoint_Throws() { - RemoteExecutor.Invoke(async (async) => + await RemoteExecutor.Invoke(async (async) => { Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); @@ -2422,7 +2420,7 @@ public void SendHttpRequest_BindIPEndPoint_Throws() socket.Dispose(); cts.Dispose(); } - }, IsAsync.ToString()).Dispose(); + }, IsAsync.ToString()).DisposeAsync(); } [Fact] diff --git a/src/libraries/System.Net.Requests/tests/ServicePointTests/ServicePointManagerTest.cs b/src/libraries/System.Net.Requests/tests/ServicePointTests/ServicePointManagerTest.cs index 33f7d1ac48ba7..04eeb67ddc1be 100644 --- a/src/libraries/System.Net.Requests/tests/ServicePointTests/ServicePointManagerTest.cs +++ b/src/libraries/System.Net.Requests/tests/ServicePointTests/ServicePointManagerTest.cs @@ -3,6 +3,7 @@ using System.Net.Security; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -195,9 +196,9 @@ public static void UseNagleAlgorithm_Roundtrips() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void InvalidArguments_Throw() + public static async Task InvalidArguments_Throw() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { const int ssl2Client = 0x00000008; const int ssl2Server = 0x00000004; @@ -221,7 +222,7 @@ public static void InvalidArguments_Throw() AssertExtensions.Throws("value", () => sp.ReceiveBufferSize = -2); AssertExtensions.Throws("keepAliveTime", () => sp.SetTcpKeepAlive(true, -1, 1)); AssertExtensions.Throws("keepAliveInterval", () => sp.SetTcpKeepAlive(true, 1, -1)); - }).Dispose(); + }).DisposeAsync(); } [Fact] @@ -246,9 +247,9 @@ public static void SecurityProtocol_Ssl3_NotSupported() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void FindServicePoint_ReturnsCachedServicePoint() + public static async Task FindServicePoint_ReturnsCachedServicePoint() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { const string Localhost = "http://localhost"; string address1 = "http://" + Guid.NewGuid().ToString("N"); @@ -278,15 +279,15 @@ public static void FindServicePoint_ReturnsCachedServicePoint() Assert.NotSame( ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address2))); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/36217", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] [ActiveIssue("https://github.com/dotnet/runtime/issues/64674", typeof(PlatformDetection), nameof(PlatformDetection.IsArmv6Process))] - public static void FindServicePoint_Collectible() + public static async Task FindServicePoint_Collectible() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { string address = "http://" + Guid.NewGuid().ToString("N"); @@ -298,13 +299,13 @@ public static void FindServicePoint_Collectible() GC.Collect(); Assert.Equal(initial, GetExpect100Continue(address)); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void FindServicePoint_ReturnedServicePointMatchesExpectedValues() + public static async Task FindServicePoint_ReturnedServicePointMatchesExpectedValues() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { string address = "http://" + Guid.NewGuid().ToString("N"); @@ -325,13 +326,13 @@ public static void FindServicePoint_ReturnedServicePointMatchesExpectedValues() Assert.Equal(-1, sp.ReceiveBufferSize); Assert.True(sp.SupportsPipelining, "SupportsPipelining"); Assert.False(sp.UseNagleAlgorithm, "UseNagleAlgorithm"); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void FindServicePoint_PropertiesRoundtrip() + public static async Task FindServicePoint_PropertiesRoundtrip() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { string address = "http://" + Guid.NewGuid().ToString("N"); @@ -360,13 +361,13 @@ public static void FindServicePoint_PropertiesRoundtrip() Assert.Equal(expectedMaxIdleTime, sp2.MaxIdleTime); Assert.Equal(expectedReceiveBufferSize, sp2.ReceiveBufferSize); Assert.Equal(expectedUseNagleAlgorithm, sp2.UseNagleAlgorithm); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void FindServicePoint_NewServicePointsInheritCurrentValues() + public static async Task FindServicePoint_NewServicePointsInheritCurrentValues() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { string address1 = "http://" + Guid.NewGuid().ToString("N"); string address2 = "http://" + Guid.NewGuid().ToString("N"); @@ -390,7 +391,7 @@ public static void FindServicePoint_NewServicePointsInheritCurrentValues() ServicePointManager.Expect100Continue = orig100Continue; ServicePointManager.UseNagleAlgorithm = origNagle; - }).Dispose(); + }).DisposeAsync(); } // Separated out to avoid the JIT in debug builds interfering with object lifetimes diff --git a/src/libraries/System.Net.Requests/tests/ServicePointTests/System.Net.ServicePoint.Tests.csproj b/src/libraries/System.Net.Requests/tests/ServicePointTests/System.Net.ServicePoint.Tests.csproj index ee7e7c37eaf88..77850da2afaef 100644 --- a/src/libraries/System.Net.Requests/tests/ServicePointTests/System.Net.ServicePoint.Tests.csproj +++ b/src/libraries/System.Net.Requests/tests/ServicePointTests/System.Net.ServicePoint.Tests.csproj @@ -9,5 +9,7 @@ + diff --git a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj index 828f168bd15f6..ea595a3dc0946 100644 --- a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj +++ b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj @@ -28,6 +28,8 @@ Link="Common\System\Net\Configuration.Security.cs" /> + + await RemoteExecutor.Invoke(() => { IWebProxy p = new WebProxy(); WebRequest.DefaultWebProxy = p; Assert.Same(p, WebRequest.DefaultWebProxy); - }).Dispose(); + }).DisposeAsync(); } [Fact] @@ -193,10 +193,10 @@ public void RegisterPrefix_DuplicateHttpWithFakeFactory_ExpectFalse() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(RequestCacheLevel.NoCacheNoStore, new string[] { "Pragma: no-cache", "Cache-Control: no-store, no-cache" })] [InlineData(RequestCacheLevel.Reload, new string[] { "Pragma: no-cache", "Cache-Control: no-cache" })] - public void SendGetRequest_WithGlobalCachePolicy_AddCacheHeaders( + public async Task SendGetRequest_WithGlobalCachePolicy_AddCacheHeaders( RequestCacheLevel requestCacheLevel, string[] expectedHeaders) { - RemoteExecutor.Invoke(async (reqCacheLevel, eh0, eh1) => + await RemoteExecutor.Invoke(async (reqCacheLevel, eh0, eh1) => { await LoopbackServer.CreateServerAsync(async (server, uri) => { @@ -216,13 +216,13 @@ await server.AcceptConnectionAsync(async connection => Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }); - }, requestCacheLevel.ToString(), expectedHeaders[0], expectedHeaders[1]).Dispose(); + }, requestCacheLevel.ToString(), expectedHeaders[0], expectedHeaders[1]).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void SendGetRequest_WithGlobalCachePolicyBypassCache_DoNotAddCacheHeaders() + public async Task SendGetRequest_WithGlobalCachePolicyBypassCache_DoNotAddCacheHeaders() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { await LoopbackServer.CreateServerAsync(async (server, uri) => { @@ -246,7 +246,7 @@ await server.AcceptConnectionAsync(async connection => Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }); - }).Dispose(); + }).DisposeAsync(); } private class FakeRequest : WebRequest diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs index 3d219c7378c8a..0558485b6c4aa 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Security.Authentication; using static Interop.SspiCli; namespace System.Net.Security @@ -66,7 +67,17 @@ public void UpdateSslConnectionInfo(SafeDeleteContext securityContext) TlsCipherSuite = cipherSuite; - ApplicationProtocol = GetNegotiatedApplicationProtocol(securityContext); + // In TLS1.3, Schannel may erroneously report empty ALPN after + // receiving resumption ticket (fake Renegotiation). Avoid updating + // ApplicationProtocol in this case if we already have some, TLS1.3 + // does not allow ALPN changes after the initial handshake. + // + // TLS 1.2 and below theoretically support ALPN changes during + // Renegotiation. + if (ApplicationProtocol == null || (Protocol & (int)SslProtocols.Tls13) == 0) + { + ApplicationProtocol = GetNegotiatedApplicationProtocol(securityContext); + } #if DEBUG SecPkgContext_SessionInfo info = default; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/LoggingTest.cs index 87de0e09dc278..52a8282409aac 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/LoggingTest.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Diagnostics.Tracing; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -25,9 +26,9 @@ public void EventSource_ExistsWithCorrectId() [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "X509 certificate store is not supported on iOS or tvOS.")] // Match SslStream_StreamToStream_Authentication_Success - public void EventSource_EventsRaisedAsExpected() + public async Task EventSource_EventsRaisedAsExpected() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { try { @@ -46,7 +47,7 @@ await listener.RunWithCallbackAsync(events.Enqueue, async () => { // Don't throw inside RemoteExecutor if SslStream_StreamToStream_Authentication_Success chose to skip the test } - }).Dispose(); + }).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs index a4882469fccc7..c441c8f70d62d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Sockets; using System.Net.Test.Common; using System.Security.Authentication; @@ -31,21 +32,6 @@ protected SslStreamAlpnTestBase(ITestOutputHelper output) _output = output; } - private async Task DoHandshakeWithOptions(SslStream clientSslStream, SslStream serverSslStream, SslClientAuthenticationOptions clientOptions, SslServerAuthenticationOptions serverOptions) - { - using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) - { - clientOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate; - clientOptions.TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false); - serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(certificate, null); - - Task t1 = clientSslStream.AuthenticateAsClientAsync(TestAuthenticateAsync, clientOptions); - Task t2 = serverSslStream.AuthenticateAsServerAsync(TestAuthenticateAsync, serverOptions); - - await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); - } - } - protected bool AllowAnyServerCertificate( object sender, X509Certificate certificate, @@ -101,28 +87,48 @@ public async Task SslStream_StreamToStream_DuplicateOptions_Throws() [Theory] [MemberData(nameof(Alpn_TestData))] - public async Task SslStream_StreamToStream_Alpn_Success(List clientProtocols, List serverProtocols, SslApplicationProtocol expected) + public async Task SslStream_StreamToStream_Alpn_Success(SslProtocols protocol, List clientProtocols, List serverProtocols, SslApplicationProtocol expected) { - (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); - using (clientStream) - using (serverStream) - using (var client = new SslStream(clientStream, false)) - using (var server = new SslStream(serverStream, false)) + using X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate(); + + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions { - SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions - { - ApplicationProtocols = clientProtocols, - }; + ApplicationProtocols = clientProtocols, + EnabledSslProtocols = protocol, + RemoteCertificateValidationCallback = delegate { return true; }, + TargetHost = Guid.NewGuid().ToString("N"), + }; - SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions + { + ApplicationProtocols = serverProtocols, + EnabledSslProtocols = protocol, + ServerCertificateContext = SslStreamCertificateContext.Create(certificate, null) + }; + + // We do multiple loops to also cover credential cache and TLS resume. + for (int i = 0; i < 3; i++) + { + (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); + using (clientStream) + using (serverStream) + using (var client = new SslStream(clientStream, false)) + using (var server = new SslStream(serverStream, false)) { - ApplicationProtocols = serverProtocols, - }; + Task t1 = client.AuthenticateAsClientAsync(TestAuthenticateAsync, clientOptions); + Task t2 = server.AuthenticateAsServerAsync(TestAuthenticateAsync, serverOptions); - await DoHandshakeWithOptions(client, server, clientOptions, serverOptions); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); + + Assert.Equal(expected, client.NegotiatedApplicationProtocol); + Assert.Equal(expected, server.NegotiatedApplicationProtocol); - Assert.Equal(expected, client.NegotiatedApplicationProtocol); - Assert.Equal(expected, server.NegotiatedApplicationProtocol); + await TestHelper.PingPong(client, server); + await TestHelper.PingPong(server, client); + + Assert.Equal(expected, client.NegotiatedApplicationProtocol); + Assert.Equal(expected, server.NegotiatedApplicationProtocol); + } } } @@ -184,7 +190,7 @@ public async Task SslStream_Http2_Alpn_Success(Uri server) { SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions { - ApplicationProtocols = new List { SslApplicationProtocol.Http2 , SslApplicationProtocol.Http11 }, + ApplicationProtocols = new List { SslApplicationProtocol.Http2, SslApplicationProtocol.Http11 }, TargetHost = server.Host }; @@ -202,16 +208,37 @@ public async Task SslStream_Http2_Alpn_Success(Uri server) public static IEnumerable Alpn_TestData() { - yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http2 : default }; - yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; - yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; - yield return new object[] { null, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, default(SslApplicationProtocol) }; - yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List(), default(SslApplicationProtocol) }; - yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null, default(SslApplicationProtocol) }; - yield return new object[] { new List(), new List(), default(SslApplicationProtocol) }; - yield return new object[] { null, new List(), default(SslApplicationProtocol) }; - yield return new object[] { new List(), null, default(SslApplicationProtocol) }; - yield return new object[] { null, null, default(SslApplicationProtocol) }; + SslApplicationProtocol h1 = SslApplicationProtocol.Http11; + SslApplicationProtocol h2 = SslApplicationProtocol.Http2; + List list_empty = []; + List list_h1 = [h1]; + List list_h2 = [h2]; + List list_both = [h1, h2]; + + foreach (var protocol in new SslProtocolSupport.SupportedSslProtocolsTestData().Concat(new[] { new object[] { SslProtocols.None } })) + { + var proto = protocol[0]; +#pragma warning disable 0618 // SSL2/3 are deprecated + if (proto.Equals(SslProtocols.Ssl3) || proto.Equals(SslProtocols.Ssl2)) +#pragma warning restore 0618 + { + // ALPN not supported by this protocol + continue; + } + + yield return new object[] { proto, list_both, list_h2, BackendSupportsAlpn ? h2 : default }; + yield return new object[] { proto, list_h1, list_both, BackendSupportsAlpn ? h1 : default }; + + yield return new object[] { proto, list_both, list_both, BackendSupportsAlpn ? h1 : default }; + yield return new object[] { proto, null, list_both, default(SslApplicationProtocol) }; + yield return new object[] { proto, list_both, list_empty, default(SslApplicationProtocol) }; + yield return new object[] { proto, list_both, null, default(SslApplicationProtocol) }; + + yield return new object[] { proto, list_empty, list_empty, default(SslApplicationProtocol) }; + yield return new object[] { proto, null, list_empty, default(SslApplicationProtocol) }; + yield return new object[] { proto, list_empty, null, default(SslApplicationProtocol) }; + yield return new object[] { proto, null, null, default(SslApplicationProtocol) }; + } } } @@ -220,7 +247,7 @@ public sealed class SslStreamAlpnTest_Async : SslStreamAlpnTestBase public override bool TestAuthenticateAsync => true; public SslStreamAlpnTest_Async(ITestOutputHelper output) - : base (output) { } + : base(output) { } } public sealed class SslStreamAlpnTest_Sync : SslStreamAlpnTestBase diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs index 82cd3114eb477..87ec6605e37aa 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs @@ -25,7 +25,7 @@ public SslStreamRemoteExecutorTests() [PlatformSpecific(TestPlatforms.Linux)] // SSLKEYLOGFILE is only supported on Linux for SslStream [InlineData(true)] [InlineData(false)] - public void SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) + public async Task SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) { if (PlatformDetection.IsDebugLibrary(typeof(SslStream).Assembly) && !enabledBySwitch) { @@ -38,7 +38,7 @@ public void SslKeyLogFile_IsCreatedAndFilled(bool enabledBySwitch) var tempFile = Path.GetTempFileName(); psi.Environment.Add("SSLKEYLOGFILE", tempFile); - RemoteExecutor.Invoke(async (enabledBySwitch) => + await RemoteExecutor.Invoke(async (enabledBySwitch) => { if (bool.Parse(enabledBySwitch)) { @@ -64,7 +64,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( await TestHelper.PingPong(client, server); } - }, enabledBySwitch.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + }, enabledBySwitch.ToString(), new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync(); if (enabledBySwitch) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index fe9c74708ad61..5fffd852c7506 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -85,6 +85,8 @@ Link="Common\System\Net\VerboseTestLogging.cs" /> + + await RemoteExecutor.Invoke(async () => { try { @@ -92,14 +92,14 @@ await listener.RunWithCallbackAsync(e => { // Don't throw inside RemoteExecutor if SslStream_StreamToStream_Authentication_Success chose to skip the test } - }).Dispose(); + }).DisposeAsync(); } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void EventSource_UnsuccessfulHandshake_LogsStartFailureStop() + public static async Task EventSource_UnsuccessfulHandshake_LogsStartFailureStop() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { using var listener = new TestEventListener("System.Net.Security", EventLevel.Verbose, eventCounterInterval: 0.1d); listener.AddActivityTracking(); @@ -159,7 +159,7 @@ await listener.RunWithCallbackAsync(e => Assert.Equal(false, clientFailure.Payload[0]); VerifyEventCounters(events, shouldHaveFailures: true); - }).Dispose(); + }).DisposeAsync(); } private static SslProtocols ValidateHandshakeStopEventPayload(EventWrittenEventArgs stopEvent, bool failure = false) diff --git a/src/libraries/System.Net.ServerSentEvents/System.Net.ServerSentEvents.sln b/src/libraries/System.Net.ServerSentEvents/System.Net.ServerSentEvents.sln new file mode 100644 index 0000000000000..a827c8cdae4fe --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/System.Net.ServerSentEvents.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.34910.147 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents", "ref\System.Net.ServerSentEvents.csproj", "{ACB7E0BF-015F-43DC-A2F5-85506173B223}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents", "src\System.Net.ServerSentEvents.csproj", "{ACDB56AF-7B9F-4762-9764-D6FF09118D09}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents.Tests", "tests\System.Net.ServerSentEvents.Tests.csproj", "{804B5D44-05A3-491E-A6AB-35C592E6703E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2BD73108-47D7-40E6-BFCB-169E6AD42A81}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{E6AF8CEE-6550-4190-97D4-D51C5B114919}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D908DCBE-EFA4-4CCA-9A1C-AEB48D59C504}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ACB7E0BF-015F-43DC-A2F5-85506173B223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACB7E0BF-015F-43DC-A2F5-85506173B223}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACB7E0BF-015F-43DC-A2F5-85506173B223}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACB7E0BF-015F-43DC-A2F5-85506173B223}.Release|Any CPU.Build.0 = Release|Any CPU + {ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Release|Any CPU.Build.0 = Release|Any CPU + {804B5D44-05A3-491E-A6AB-35C592E6703E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {804B5D44-05A3-491E-A6AB-35C592E6703E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {804B5D44-05A3-491E-A6AB-35C592E6703E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {804B5D44-05A3-491E-A6AB-35C592E6703E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {ACB7E0BF-015F-43DC-A2F5-85506173B223} = {E6AF8CEE-6550-4190-97D4-D51C5B114919} + {ACDB56AF-7B9F-4762-9764-D6FF09118D09} = {D908DCBE-EFA4-4CCA-9A1C-AEB48D59C504} + {804B5D44-05A3-491E-A6AB-35C592E6703E} = {2BD73108-47D7-40E6-BFCB-169E6AD42A81} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {01DAF96B-AF8E-4576-A1BC-57D19BDB317E} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.cs b/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.cs new file mode 100644 index 0000000000000..994108c08e6c3 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Net.ServerSentEvents +{ + public delegate T SseItemParser(string eventType, System.ReadOnlySpan data); + public readonly partial struct SseItem + { + private readonly T _Data_k__BackingField; + private readonly object _dummy; + private readonly int _dummyPrimitive; + public SseItem(T data, string eventType) { throw null; } + public T Data { get { throw null; } } + public string EventType { get { throw null; } } + } + public static partial class SseParser + { + public const string EventTypeDefault = "message"; + public static System.Net.ServerSentEvents.SseParser Create(System.IO.Stream sseStream) { throw null; } + public static System.Net.ServerSentEvents.SseParser Create(System.IO.Stream sseStream, System.Net.ServerSentEvents.SseItemParser itemParser) { throw null; } + } + public sealed partial class SseParser + { + internal SseParser() { } + public string LastEventId { get { throw null; } } + public System.TimeSpan ReconnectionInterval { get { throw null; } } + public System.Collections.Generic.IEnumerable> Enumerate() { throw null; } + public System.Collections.Generic.IAsyncEnumerable> EnumerateAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } +} diff --git a/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.csproj b/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.csproj new file mode 100644 index 0000000000000..114d16ec0664b --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/ref/System.Net.ServerSentEvents.csproj @@ -0,0 +1,16 @@ + + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + + + + + + + + + + + + diff --git a/src/libraries/System.Net.ServerSentEvents/src/PACKAGE.md b/src/libraries/System.Net.ServerSentEvents/src/PACKAGE.md new file mode 100644 index 0000000000000..95ad1893305b8 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/PACKAGE.md @@ -0,0 +1,52 @@ +## About + + + +System.Net.ServerSentEvents provides the `SseParser` type, which exposes factory methods for creating parsers for the events in a stream of server-sent events (SSE). + +## Key Features + + + +* Parser for server-sent events (SSE) + +## How to Use + + + +Asynchronously parsing event contents as strings + +```csharp +using HttpClient client = new(); +using Stream stream = await client.GetStreamAsync("https://localhost:12345/sse"); +await foreach (SseItem item in SseParser.Create(stream).EnumerateAsync()) +{ + Console.WriteLine(item.Data); +} +``` + +Synchronously parsing event contents as JSON + +```csharp +MemoryStream stream = new(data); +foreach (SseItem item in SseParser.Create(stream, (eventType, bytes) => JsonSerializer.Deserialize(bytes)).Enumerate()) +{ + Console.WriteLine(item.Data.Author); +} +``` + +## Main Types + + + +The main types provided by this library are: + +* `System.Net.ServerSentEvents.SseParser` +* `System.Net.ServerSentEvents.SseParser` +* `System.Net.ServerSentEvents.SseItem` + +## Feedback & Contributing + + + +System.Net.ServerSentEvents 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). diff --git a/src/libraries/System.Net.ServerSentEvents/src/Resources/Strings.resx b/src/libraries/System.Net.ServerSentEvents/src/Resources/Strings.resx new file mode 100644 index 0000000000000..a0ea42d131d14 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/Resources/Strings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The enumerable may be enumerated only once. + + diff --git a/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj b/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj new file mode 100644 index 0000000000000..b57f9e5c50739 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csproj @@ -0,0 +1,32 @@ + + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + true + true + true + Provides a simple parser for server-sent events (SSE). + +Commonly Used Types: +System.Net.ServerSentEvents.SseParser + + + true + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItem.cs b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItem.cs new file mode 100644 index 0000000000000..061ed89e42ae8 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItem.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Net.ServerSentEvents +{ + /// Represents a server-sent event. + /// Specifies the type of data payload in the event. + public readonly struct SseItem + { + /// Initializes the server-sent event. + /// The event's payload. + /// The event's type. + public SseItem(T data, string eventType) + { + Data = data; + EventType = eventType; + } + + /// Gets the event's payload. + public T Data { get; } + + /// Gets the event's type. + public string EventType { get; } + } +} diff --git a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItemParser.cs b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItemParser.cs new file mode 100644 index 0000000000000..67654dc5cd24f --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseItemParser.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Net.ServerSentEvents +{ + /// Encapsulates a method for parsing the bytes payload of a server-sent event. + /// Specifies the type of the return value of the parser. + /// The event's type. + /// The event's payload bytes. + /// The parsed . + public delegate T SseItemParser(string eventType, ReadOnlySpan data); +} diff --git a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser.cs b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser.cs new file mode 100644 index 0000000000000..26543247aacff --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser.cs @@ -0,0 +1,58 @@ +// 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.Text; +using System.Threading; + +namespace System.Net.ServerSentEvents +{ + /// Provides a parser for parsing server-sent events. + public static class SseParser + { + /// The default ("message") for an event that did not explicitly specify a type. + public const string EventTypeDefault = "message"; + + /// Creates a parser for parsing a of server-sent events into a sequence of values. + /// The stream containing the data to parse. + /// + /// The enumerable of strings, which may be enumerated synchronously or asynchronously. The strings + /// are decoded from the UTF8-encoded bytes of the payload of each event. + /// + /// is null. + /// + /// This overload has behavior equivalent to calling with a delegate + /// that decodes the data of each event using 's GetString method. + /// + public static SseParser Create(Stream sseStream) => + Create(sseStream, static (_, bytes) => Utf8GetString(bytes)); + + /// Creates a parser for parsing a of server-sent events into a sequence of values. + /// Specifies the type of data in each event. + /// The stream containing the data to parse. + /// The parser to use to transform each payload of bytes into a data element. + /// The enumerable, which may be enumerated synchronously or asynchronously. + /// is null. + /// is null. + public static SseParser Create(Stream sseStream, SseItemParser itemParser) => + new SseParser( + sseStream ?? throw new ArgumentNullException(nameof(sseStream)), + itemParser ?? throw new ArgumentNullException(nameof(itemParser))); + + /// Encoding.UTF8.GetString(bytes) + internal static unsafe string Utf8GetString(ReadOnlySpan bytes) + { +#if NET + return Encoding.UTF8.GetString(bytes); +#else + fixed (byte* ptr = bytes) + { + return ptr is null ? + string.Empty : + Encoding.UTF8.GetString(ptr, bytes.Length); + } +#endif + } + } +} diff --git a/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs new file mode 100644 index 0000000000000..98beedb7048fa --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/src/System/Net/ServerSentEvents/SseParser_1.cs @@ -0,0 +1,533 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.ServerSentEvents +{ + /// Provides a parser for server-sent events information. + /// Specifies the type of data parsed from an event. + public sealed class SseParser + { + // For reference: + // Specification: https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events + + /// Carriage Return. + private const byte CR = (byte)'\r'; + /// Line Feed. + private const byte LF = (byte)'\n'; + /// Carriage Return Line Feed. + private static ReadOnlySpan CRLF => "\r\n"u8; + + /// The default size of an ArrayPool buffer to rent. + /// Larger size used by default to minimize number of reads. Smaller size used in debug to stress growth/shifting logic. + private const int DefaultArrayPoolRentSize = +#if DEBUG + 16; +#else + 1024; +#endif + + /// The stream to be parsed. + private readonly Stream _stream; + /// The parser delegate used to transform bytes into a . + private readonly SseItemParser _itemParser; + + /// Indicates whether the enumerable has already been used for enumeration. + private int _used; + + /// Buffer, either empty or rented, containing the data being read from the stream while looking for the next line. + private byte[] _lineBuffer = []; + /// The starting offset of valid data in . + private int _lineOffset; + /// The length of valid data in , starting from . + private int _lineLength; + /// The index in where a newline ('\r', '\n', or "\r\n") was found. + private int _newlineIndex; + /// The index in of characters already checked for newlines. + /// + /// This is to avoid O(LineLength^2) behavior in the rare case where we have long lines that are built-up over multiple reads. + /// We want to avoid re-checking the same characters we've already checked over and over again. + /// + private int _lastSearchedForNewline; + /// Set when eof has been reached in the stream. + private bool _eof; + + /// Rented buffer containing buffered data for the next event. + private byte[]? _dataBuffer; + /// The length of valid data in , starting from index 0. + private int _dataLength; + /// Whether data has been appended to . + /// This can be different than != 0 if empty data was appended. + private bool _dataAppended; + + /// The event type for the next event. + private string _eventType = SseParser.EventTypeDefault; + + /// Initialize the enumerable. + /// The stream to parse. + /// The function to use to parse payload bytes into a . + internal SseParser(Stream stream, SseItemParser itemParser) + { + _stream = stream; + _itemParser = itemParser; + } + + /// Gets an enumerable of the server-sent events from this parser. + /// The parser has already been enumerated. Such an exception may propagate out of a call to . + public IEnumerable> Enumerate() + { + // Validate that the parser is only used for one enumeration. + ThrowIfNotFirstEnumeration(); + + // Rent a line buffer. This will grow as needed. The line buffer is what's passed to the stream, + // so we want it to be large enough to reduce the number of reads we need to do when data is + // arriving quickly. (In debug, we use a smaller buffer to stress the growth and shifting logic.) + _lineBuffer = ArrayPool.Shared.Rent(DefaultArrayPoolRentSize); + try + { + // Spec: "Event streams in this format must always be encoded as UTF-8". + // Skip a UTF8 BOM if it exists at the beginning of the stream. (The BOM is defined as optional in the SSE grammar.) + while (FillLineBuffer() != 0 && _lineLength < Utf8Bom.Length) ; + SkipBomIfPresent(); + + // Process all events in the stream. + while (true) + { + // See if there's a complete line in data already read from the stream. Lines are permitted to + // end with CR, LF, or CRLF. Look for all of them and if we find one, process the line. However, + // if we only find a CR and it's at the end of the read data, don't process it now, as we want + // to process it together with an LF that might immediately follow, rather than treating them + // as two separate characters, in which case we'd incorrectly process the CR as a line by itself. + GetNextSearchOffsetAndLength(out int searchOffset, out int searchLength); + _newlineIndex = _lineBuffer.AsSpan(searchOffset, searchLength).IndexOfAny(CR, LF); + if (_newlineIndex >= 0) + { + _lastSearchedForNewline = -1; + _newlineIndex += searchOffset; + if (_lineBuffer[_newlineIndex] is LF || // the newline is LF + _newlineIndex - _lineOffset + 1 < _lineLength || // we must have CR and we have whatever comes after it + _eof) // if we get here, we know we have a CR at the end of the buffer, so it's definitely the whole newline if we've hit EOF + { + // Process the line. + if (ProcessLine(out SseItem sseItem, out int advance)) + { + yield return sseItem; + } + + // Move past the line. + _lineOffset += advance; + _lineLength -= advance; + continue; + } + } + else + { + // Record the last position searched for a newline. The next time we search, + // we'll search from here rather than from _lineOffset, in order to avoid searching + // the same characters again. + _lastSearchedForNewline = _lineOffset + _lineLength; + } + + // We've processed everything in the buffer we currently can, so if we've already read EOF, we're done. + if (_eof) + { + // Spec: "Once the end of the file is reached, any pending data must be discarded. (If the file ends in the middle of an + // event, before the final empty line, the incomplete event is not dispatched.)" + break; + } + + // Read more data into the buffer. + FillLineBuffer(); + } + } + finally + { + ArrayPool.Shared.Return(_lineBuffer); + if (_dataBuffer is not null) + { + ArrayPool.Shared.Return(_dataBuffer); + } + } + } + + /// Gets an asynchronous enumerable of the server-sent events from this parser. + /// The cancellation token to use to cancel the enumeration. + /// The parser has already been enumerated. Such an exception may propagate out of a call to . + /// The enumeration was canceled. Such an exception may propagate out of a call to . + public async IAsyncEnumerable> EnumerateAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + { + // Validate that the parser is only used for one enumeration. + ThrowIfNotFirstEnumeration(); + + // Rent a line buffer. This will grow as needed. The line buffer is what's passed to the stream, + // so we want it to be large enough to reduce the number of reads we need to do when data is + // arriving quickly. (In debug, we use a smaller buffer to stress the growth and shifting logic.) + _lineBuffer = ArrayPool.Shared.Rent(DefaultArrayPoolRentSize); + try + { + // Spec: "Event streams in this format must always be encoded as UTF-8". + // Skip a UTF8 BOM if it exists at the beginning of the stream. (The BOM is defined as optional in the SSE grammar.) + while (await FillLineBufferAsync(cancellationToken).ConfigureAwait(false) != 0 && _lineLength < Utf8Bom.Length) ; + SkipBomIfPresent(); + + // Process all events in the stream. + while (true) + { + // See if there's a complete line in data already read from the stream. Lines are permitted to + // end with CR, LF, or CRLF. Look for all of them and if we find one, process the line. However, + // if we only find a CR and it's at the end of the read data, don't process it now, as we want + // to process it together with an LF that might immediately follow, rather than treating them + // as two separate characters, in which case we'd incorrectly process the CR as a line by itself. + GetNextSearchOffsetAndLength(out int searchOffset, out int searchLength); + _newlineIndex = _lineBuffer.AsSpan(searchOffset, searchLength).IndexOfAny(CR, LF); + if (_newlineIndex >= 0) + { + _lastSearchedForNewline = -1; + _newlineIndex += searchOffset; + if (_lineBuffer[_newlineIndex] is LF || // newline is LF + _newlineIndex - _lineOffset + 1 < _lineLength || // newline is CR, and we have whatever comes after it + _eof) // if we get here, we know we have a CR at the end of the buffer, so it's definitely the whole newline if we've hit EOF + { + // Process the line. + if (ProcessLine(out SseItem sseItem, out int advance)) + { + yield return sseItem; + } + + // Move past the line. + _lineOffset += advance; + _lineLength -= advance; + continue; + } + } + else + { + // Record the last position searched for a newline. The next time we search, + // we'll search from here rather than from _lineOffset, in order to avoid searching + // the same characters again. + _lastSearchedForNewline = searchOffset + searchLength; + } + + // We've processed everything in the buffer we currently can, so if we've already read EOF, we're done. + if (_eof) + { + // Spec: "Once the end of the file is reached, any pending data must be discarded. (If the file ends in the middle of an + // event, before the final empty line, the incomplete event is not dispatched.)" + break; + } + + // Read more data into the buffer. + await FillLineBufferAsync(cancellationToken).ConfigureAwait(false); + } + } + finally + { + ArrayPool.Shared.Return(_lineBuffer); + if (_dataBuffer is not null) + { + ArrayPool.Shared.Return(_dataBuffer); + } + } + } + + /// Gets the next index and length with which to perform a newline search. + private void GetNextSearchOffsetAndLength(out int searchOffset, out int searchLength) + { + if (_lastSearchedForNewline > _lineOffset) + { + searchOffset = _lastSearchedForNewline; + searchLength = _lineLength - (_lastSearchedForNewline - _lineOffset); + } + else + { + searchOffset = _lineOffset; + searchLength = _lineLength; + } + + Debug.Assert(searchOffset >= _lineOffset, $"{searchOffset}, {_lineLength}"); + Debug.Assert(searchOffset <= _lineOffset + _lineLength, $"{searchOffset}, {_lineOffset}, {_lineLength}"); + Debug.Assert(searchOffset <= _lineBuffer.Length, $"{searchOffset}, {_lineBuffer.Length}"); + + Debug.Assert(searchLength >= 0, $"{searchLength}"); + Debug.Assert(searchLength <= _lineLength, $"{searchLength}, {_lineLength}"); + } + + private int GetNewLineLength() + { + Debug.Assert(_newlineIndex - _lineOffset < _lineLength, "Expected to be positioned at a non-empty newline"); + return _lineBuffer.AsSpan(_newlineIndex, _lineLength - (_newlineIndex - _lineOffset)).StartsWith(CRLF) ? 2 : 1; + } + + /// + /// If there's no room remaining in the line buffer, either shifts the contents + /// left or grows the buffer in order to make room for the next read. + /// + private void ShiftOrGrowLineBufferIfNecessary() + { + // If data we've read is butting up against the end of the buffer and + // it's not taking up the entire buffer, slide what's there down to + // the beginning, making room to read more data into the buffer (since + // there's no newline in the data that's there). Otherwise, if the whole + // buffer is full, grow the buffer to accommodate more data, since, again, + // what's there doesn't contain a newline and thus a line is longer than + // the current buffer accommodates. + if (_lineOffset + _lineLength == _lineBuffer.Length) + { + if (_lineOffset != 0) + { + _lineBuffer.AsSpan(_lineOffset, _lineLength).CopyTo(_lineBuffer); + if (_lastSearchedForNewline >= 0) + { + _lastSearchedForNewline -= _lineOffset; + } + _lineOffset = 0; + } + else if (_lineLength == _lineBuffer.Length) + { + GrowBuffer(ref _lineBuffer, _lineBuffer.Length * 2); + } + } + } + + /// Processes a complete line from the SSE stream. + /// The parsed item if the method returns true. + /// How many characters to advance in the line buffer. + /// true if an SSE item was successfully parsed; otherwise, false. + private bool ProcessLine(out SseItem sseItem, out int advance) + { + ReadOnlySpan line = _lineBuffer.AsSpan(_lineOffset, _newlineIndex - _lineOffset); + + // Spec: "If the line is empty (a blank line) Dispatch the event" + if (line.IsEmpty) + { + advance = GetNewLineLength(); + + if (_dataAppended) + { + sseItem = new SseItem(_itemParser(_eventType, _dataBuffer.AsSpan(0, _dataLength)), _eventType); + _eventType = SseParser.EventTypeDefault; + _dataLength = 0; + _dataAppended = false; + return true; + } + + sseItem = default; + return false; + } + + // Find the colon separating the field name and value. + int colonPos = line.IndexOf((byte)':'); + ReadOnlySpan fieldName; + ReadOnlySpan fieldValue; + if (colonPos >= 0) + { + // Spec: "Collect the characters on the line before the first U+003A COLON character (:), and let field be that string." + fieldName = line.Slice(0, colonPos); + + // Spec: "Collect the characters on the line after the first U+003A COLON character (:), and let value be that string. + // If value starts with a U+0020 SPACE character, remove it from value." + fieldValue = line.Slice(colonPos + 1); + if (!fieldValue.IsEmpty && fieldValue[0] == (byte)' ') + { + fieldValue = fieldValue.Slice(1); + } + } + else + { + // Spec: "using the whole line as the field name, and the empty string as the field value." + fieldName = line; + fieldValue = []; + } + + if (fieldName.SequenceEqual("data"u8)) + { + // Spec: "Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to the data buffer." + // Spec: "If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer." + + // If there's nothing currently in the data buffer and we can easily detect that this line is immediately followed by + // an empty line, we can optimize it to just handle the data directly from the line buffer, rather than first copying + // into the data buffer and dispatching from there. + if (!_dataAppended) + { + int newlineLength = GetNewLineLength(); + ReadOnlySpan remainder = _lineBuffer.AsSpan(_newlineIndex + newlineLength, _lineLength - line.Length - newlineLength); + if (!remainder.IsEmpty && + (remainder[0] is LF || (remainder[0] is CR && remainder.Length > 1))) + { + advance = line.Length + newlineLength + (remainder.StartsWith(CRLF) ? 2 : 1); + sseItem = new SseItem(_itemParser(_eventType, fieldValue), _eventType); + _eventType = SseParser.EventTypeDefault; + return true; + } + } + + // We need to copy the data from the data buffer to the line buffer. Make sure there's enough room. + if (_dataBuffer is null || _dataLength + _lineLength + 1 > _dataBuffer.Length) + { + GrowBuffer(ref _dataBuffer, _dataLength + _lineLength + 1); + } + + // Append a newline if there's already content in the buffer. + // Then copy the field value to the data buffer + if (_dataAppended) + { + _dataBuffer[_dataLength++] = LF; + } + fieldValue.CopyTo(_dataBuffer.AsSpan(_dataLength)); + _dataLength += fieldValue.Length; + _dataAppended = true; + } + else if (fieldName.SequenceEqual("event"u8)) + { + // Spec: "Set the event type buffer to field value." + _eventType = SseParser.Utf8GetString(fieldValue); + } + else if (fieldName.SequenceEqual("id"u8)) + { + // Spec: "If the field value does not contain U+0000 NULL, then set the last event ID buffer to the field value. Otherwise, ignore the field." + if (fieldValue.IndexOf((byte)'\0') < 0) + { + // Note that fieldValue might be empty, in which case LastEventId will naturally be reset to the empty string. This is per spec. + LastEventId = SseParser.Utf8GetString(fieldValue); + } + } + else if (fieldName.SequenceEqual("retry"u8)) + { + // Spec: "If the field value consists of only ASCII digits, then interpret the field value as an integer in base ten, + // and set the event stream's reconnection time to that integer. Otherwise, ignore the field." + if (long.TryParse( +#if NET + fieldValue, +#else + SseParser.Utf8GetString(fieldValue), +#endif + NumberStyles.None, CultureInfo.InvariantCulture, out long milliseconds)) + { + ReconnectionInterval = TimeSpan.FromMilliseconds(milliseconds); + } + } + else + { + // We'll end up here if the line starts with a colon, producing an empty field name, or if the field name is otherwise unrecognized. + // Spec: "If the line starts with a U+003A COLON character (:) Ignore the line." + // Spec: "Otherwise, The field is ignored" + } + + advance = line.Length + GetNewLineLength(); + sseItem = default; + return false; + } + + /// Gets the last event ID. + /// This value is updated any time a new last event ID is parsed. It is not reset between SSE items. + public string LastEventId { get; private set; } = string.Empty; // Spec: "must be initialized to the empty string" + + /// Gets the reconnection interval. + /// + /// If no retry event was received, this defaults to , and it will only + /// ever be in that situation. If a client wishes to retry, the server-sent + /// events specification states that the interval may then be decided by the client implementation and should be a + /// few seconds. + /// + public TimeSpan ReconnectionInterval { get; private set; } = Timeout.InfiniteTimeSpan; + + /// Transitions the object to a used state, throwing if it's already been used. + private void ThrowIfNotFirstEnumeration() + { + if (Interlocked.Exchange(ref _used, 1) != 0) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumerateOnlyOnce); + } + } + + /// Reads data from the stream into the line buffer. + private int FillLineBuffer() + { + ShiftOrGrowLineBufferIfNecessary(); + + int offset = _lineOffset + _lineLength; + int bytesRead = _stream.Read( +#if NET + _lineBuffer.AsSpan(offset)); +#else + _lineBuffer, offset, _lineBuffer.Length - offset); +#endif + + if (bytesRead > 0) + { + _lineLength += bytesRead; + } + else + { + _eof = true; + bytesRead = 0; + } + + return bytesRead; + } + + /// Reads data asynchronously from the stream into the line buffer. + private async ValueTask FillLineBufferAsync(CancellationToken cancellationToken) + { + ShiftOrGrowLineBufferIfNecessary(); + + int offset = _lineOffset + _lineLength; + int bytesRead = await +#if NET + _stream.ReadAsync(_lineBuffer.AsMemory(offset), cancellationToken) +#else + new ValueTask(_stream.ReadAsync(_lineBuffer, offset, _lineBuffer.Length - offset, cancellationToken)) +#endif + .ConfigureAwait(false); + + if (bytesRead > 0) + { + _lineLength += bytesRead; + } + else + { + _eof = true; + bytesRead = 0; + } + + return bytesRead; + } + + /// Gets the UTF8 BOM. + private static ReadOnlySpan Utf8Bom => [0xEF, 0xBB, 0xBF]; + + /// Called at the beginning of processing to skip over an optional UTF8 byte order mark. + private void SkipBomIfPresent() + { + Debug.Assert(_lineOffset == 0, $"Expected _lineOffset == 0, got {_lineOffset}"); + + if (_lineBuffer.AsSpan(0, _lineLength).StartsWith(Utf8Bom)) + { + _lineOffset += 3; + _lineLength -= 3; + } + } + + /// Grows the buffer, returning the existing one to the ArrayPool and renting an ArrayPool replacement. + private static void GrowBuffer([NotNull] ref byte[]? buffer, int minimumLength) + { + byte[]? toReturn = buffer; + buffer = ArrayPool.Shared.Rent(Math.Max(minimumLength, DefaultArrayPoolRentSize)); + if (toReturn is not null) + { + Array.Copy(toReturn, buffer, toReturn.Length); + ArrayPool.Shared.Return(toReturn); + } + } + } +} diff --git a/src/libraries/System.Net.ServerSentEvents/tests/SseParserTests.cs b/src/libraries/System.Net.ServerSentEvents/tests/SseParserTests.cs new file mode 100644 index 0000000000000..d8a8c3b37c1c6 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/tests/SseParserTests.cs @@ -0,0 +1,963 @@ +// 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; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Net.ServerSentEvents.Tests +{ + public partial class SseParserTests + { + [Fact] + public void Parse_InvalidArguments_Throws() + { + AssertExtensions.Throws("sseStream", () => SseParser.Create(null)); + AssertExtensions.Throws("sseStream", () => SseParser.Create(null, delegate { return ""; })); + AssertExtensions.Throws("itemParser", () => SseParser.Create(Stream.Null, null)); + } + + [Fact] + public async Task Parse_Sync_SupportsOnlyOneEnumeration_Throws() + { + SseParser parser = SseParser.Create(Stream.Null); + parser.Enumerate().GetEnumerator().MoveNext(); + var e = parser.Enumerate().GetEnumerator(); + var ea = parser.EnumerateAsync().GetAsyncEnumerator(); + Assert.Throws(() => e.MoveNext()); + await Assert.ThrowsAsync(async () => await ea.MoveNextAsync()); + } + + [Fact] + public async Task Parse_Async_SupportsOnlyOneEnumeration_Throws() + { + SseParser parser = SseParser.Create(Stream.Null); + await parser.EnumerateAsync().GetAsyncEnumerator().MoveNextAsync(); + var ea = parser.EnumerateAsync().GetAsyncEnumerator(); + var e = parser.Enumerate().GetEnumerator(); + await Assert.ThrowsAsync(async () => await ea.MoveNextAsync()); + Assert.Throws(() => e.MoveNext()); + } + + [Fact] + public void SseItem_Roundtrips() + { + SseItem item; + + item = new SseItem(); + Assert.Null(item.EventType); + Assert.Null(item.Data); + + item = new SseItem("some data", "eventType"); + Assert.Equal("eventType", item.EventType); + Assert.Equal("some data", item.Data); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_Empty_NoItems(string newline, bool trickle, bool useAsync) + { + _ = newline; + + using Stream stream = GetStream("", trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + Assert.Equal(stream.Length, stream.Position); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_BlankLine_NoItems(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream(newline, trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + Assert.Equal(stream.Length, stream.Position); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_TwoBlankLines_NoItems(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream(newline + newline, trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + Assert.Equal(stream.Length, stream.Position); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_MultipleBlankLinesBetweenItems_AllItemsProduces(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"event:A{newline}" + + $"data:1{newline}" + + $"id:2{newline}" + + $"retry:300{newline}" + + $"{newline}{newline}{newline}{newline}{newline}" + + + $"event:B{newline}" + + $"data:4{newline}" + + $"id:5{newline}" + + $"retry:600{newline}" + + $"{newline}{newline}{newline}{newline}{newline}" + + + $"event:C{newline}" + + $"data:7{newline}" + + $"id:8{newline}" + + $"retry:900{newline}" + + $"{newline}{newline}{newline}{newline}{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + Assert.Equal(stream.Length, stream.Position); + + Assert.Equal(3, items.Count); + AssertSseItemEqual(new SseItem("1", "A"), items[0]); + AssertSseItemEqual(new SseItem("4", "B"), items[1]); + AssertSseItemEqual(new SseItem("7", "C"), items[2]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example1(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data: This is the first message.{newline}" + + $"{newline}" + + $"data: This is the second message, it{newline}" + + $"data: has two lines.{newline}" + + $"{newline}" + + $"data: This is the third message.{newline}" + + $"{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(3, items.Count); + AssertSseItemEqual(new SseItem("This is the first message.", "message"), items[0]); + AssertSseItemEqual(new SseItem("This is the second message, it\nhas two lines.", "message"), items[1]); + AssertSseItemEqual(new SseItem("This is the third message.", "message"), items[2]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example2(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"event: add{newline}data: 73857293{newline}" + + $"{newline}" + + $"event: remove{newline}data: 2153{newline}" + + $"{newline}" + + $"event: add{newline}data: 113411{newline}" + + $"{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(3, items.Count); + AssertSseItemEqual(new SseItem("73857293", "add"), items[0]); + AssertSseItemEqual(new SseItem("2153", "remove"), items[1]); + AssertSseItemEqual(new SseItem("113411", "add"), items[2]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example3(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data: YHOO{newline}" + + $"data: +2{newline}" + + $"data: 10{newline}" + + $"{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(1, items.Count); + AssertSseItemEqual(new SseItem("YHOO\n+2\n10", "message"), items[0]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example4(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $": test stream{newline}" + + $"{newline}" + + $"data: first event{newline}" + + $"id: 1{newline}" + + $"{newline}" + + $"data:second event{newline}" + + $"id{newline}" + + $"{newline}" + + $"data: third event{newline}" + + $"{newline}", + trickle); + + SseParser parser = SseParser.Create(stream); + if (useAsync) + { + Assert.Equal(string.Empty, parser.LastEventId); + + using IEnumerator> e = parser.Enumerate().GetEnumerator(); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal(string.Empty, parser.LastEventId); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal(string.Empty, parser.LastEventId); + } + else + { + Assert.Equal(string.Empty, parser.LastEventId); + + await using IAsyncEnumerator> e = parser.EnumerateAsync().GetAsyncEnumerator(); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal(string.Empty, parser.LastEventId); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal(string.Empty, parser.LastEventId); + } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example4_InheritedIDs(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $": test stream{newline}" + + $"{newline}" + + $"data: first event{newline}" + + $"id: 1{newline}" + + $"{newline}" + + $"data:second event{newline}" + + $"{newline}" + + $"data: third event{newline}" + + $"id: 42{newline}" + + $"{newline}", + trickle); + + SseParser parser = SseParser.Create(stream); + if (useAsync) + { + Assert.Equal(string.Empty, parser.LastEventId); + + using IEnumerator> e = parser.Enumerate().GetEnumerator(); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal("42", parser.LastEventId); + } + else + { + Assert.Equal(string.Empty, parser.LastEventId); + + await using IAsyncEnumerator> e = parser.EnumerateAsync().GetAsyncEnumerator(); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal("1", parser.LastEventId); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal("42", parser.LastEventId); + } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example5(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data{newline}" + + $"{newline}" + + $"data{newline}" + + $"data{newline}" + + $"{newline}" + + $"data:", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(2, items.Count); + AssertSseItemEqual(new SseItem("", "message"), items[0]); + AssertSseItemEqual(new SseItem("\n", "message"), items[1]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example5_WithColon(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data{newline}" + + $"{newline}" + + $"data:{newline}" + + $"data{newline}" + + $"{newline}" + + $"data:", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(2, items.Count); + AssertSseItemEqual(new SseItem("", "message"), items[0]); + AssertSseItemEqual(new SseItem("\n", "message"), items[1]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Parse_HtmlSpec_Example6(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data:test{newline}" + + $"{newline}" + + $"data: test{newline}" + + $"{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(2, items.Count); + AssertSseItemEqual(new SseItem("test", "message"), items[0]); + AssertSseItemEqual(new SseItem("test", "message"), items[1]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Delegate_EventTypeArgument_MatchesValueFromEvent(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"event: add{newline}data: 73857293{newline}" + + $"{newline}" + + $"event: remove{newline}data: 2153{newline}" + + $"{newline}" + + $"event: add{newline}data: 113411{newline}" + + $"{newline}", + trickle); + + SseItemParser itemParser = (eventType, bytes) => eventType; + + List> items = useAsync ? + await ReadAllEventsAsync(stream, itemParser) : + ReadAllEvents(stream, itemParser); + + Assert.Equal(3, items.Count); + AssertSseItemEqual(new SseItem("add", "add"), items[0]); + AssertSseItemEqual(new SseItem("remove", "remove"), items[1]); + AssertSseItemEqual(new SseItem("add", "add"), items[2]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Retry_SetsReconnectionInterval(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $": test stream{newline}" + + $"{newline}" + + $"data: first event{newline}" + + $"{newline}" + + $"data:second event{newline}" + + $"retry: 42{newline}" + + $"{newline}" + + $"data: third event{newline}" + + $"retry: 12345678910{newline}" + + $"{newline}" + + $"data:fourth event{newline}" + + $"{newline}" + + $"data:fifth event{newline}" + + $"retry: invalidmilliseconds{newline}" + + $"{newline}", + trickle); + + SseParser parser = SseParser.Create(stream); + Assert.Equal(Timeout.InfiniteTimeSpan, parser.ReconnectionInterval); + + if (useAsync) + { + Assert.Equal(string.Empty, parser.LastEventId); + + using IEnumerator> e = parser.Enumerate().GetEnumerator(); + Assert.Equal(Timeout.InfiniteTimeSpan, parser.ReconnectionInterval); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + Assert.Equal(Timeout.InfiniteTimeSpan, parser.ReconnectionInterval); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(42), parser.ReconnectionInterval); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("fourth event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("fifth event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + } + else + { + Assert.Equal(string.Empty, parser.LastEventId); + + await using IAsyncEnumerator> e = parser.EnumerateAsync().GetAsyncEnumerator(); + Assert.Equal(Timeout.InfiniteTimeSpan, parser.ReconnectionInterval); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("first event", "message"), e.Current); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("second event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(42), parser.ReconnectionInterval); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem(" third event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("fourth event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + + Assert.True(await e.MoveNextAsync()); + AssertSseItemEqual(new SseItem("fifth event", "message"), e.Current); + Assert.Equal(TimeSpan.FromMilliseconds(12345678910), parser.ReconnectionInterval); + } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task JsonContent_DelegateInvoked(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream( + $"data: {{{newline}" + + $"data: \"title\": \"The Catcher in the Rye\",{newline}" + + $"data: \"author\": \"J.D. Salinger\",{newline}" + + $"data: \"published_year\": 1951,{newline}" + + $"data: \"genre\": \"Fiction\"{newline}" + + $"data: }}{newline}" + + $"{newline}" + + $"data: {{{newline}" + + $"data: \"title\": \"1984\",{newline}" + + $"data: \"author\": \"George Orwell\",{newline}" + + $"data: \"published_year\": 1949,{newline}" + + $"data: \"genre\": \"Fiction\"{newline}" + + $"data: }}{newline}" + + $"{newline}", + trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream, static (eventType, data) => JsonSerializer.Deserialize(data)) : + ReadAllEvents(stream, static (eventType, data) => JsonSerializer.Deserialize(data)); + + Assert.Equal(2, items.Count); + AssertSseItemEqual(new SseItem(new Book { title = "The Catcher in the Rye", author = "J.D. Salinger", published_year = 1951, genre = "Fiction" }, "message"), items[0]); + AssertSseItemEqual(new SseItem(new Book { title = "1984", author = "George Orwell", published_year = 1949, genre = "Fiction" }, "message"), items[1]); + } + + private struct Book + { + public string title { get; set; } + public string author { get; set; } + public int published_year { get; set; } + public string genre { get; set; } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Valid_Skipped(string newline, bool trickle, bool useAsync) + { + byte[] newlineBytes = Encoding.UTF8.GetBytes(newline); + using Stream stream = GetStream( + [ + 0xEF, 0xBB, 0xBF, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'h', (byte)'i', + ..newlineBytes, + ..newlineBytes, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'t', (byte)'h', (byte)'e', (byte)'r', (byte)'e', + ..newlineBytes, + ..newlineBytes, + ], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(2, items.Count); + AssertSseItemEqual(new SseItem("hi", "message"), items[0]); + AssertSseItemEqual(new SseItem("there", "message"), items[1]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Partial1_LineSkipped(string newline, bool trickle, bool useAsync) + { + byte[] newlineBytes = Encoding.UTF8.GetBytes(newline); + using Stream stream = GetStream( + [ + 0xEF, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'h', (byte)'i', + ..newlineBytes, + ..newlineBytes, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'t', (byte)'h', (byte)'e', (byte)'r', (byte)'e', + ..newlineBytes, + ..newlineBytes, + ], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(1, items.Count); + AssertSseItemEqual(new SseItem("there", "message"), items[0]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Partial2_LineSkipped(string newline, bool trickle, bool useAsync) + { + byte[] newlineBytes = Encoding.UTF8.GetBytes(newline); + using Stream stream = GetStream( + [ + 0xEF, 0xBB, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'h', (byte)'i', + ..newlineBytes, + ..newlineBytes, + (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', + (byte)'t', (byte)'h', (byte)'e', (byte)'r', (byte)'e', + ..newlineBytes, + ..newlineBytes, + ], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(1, items.Count); + AssertSseItemEqual(new SseItem("there", "message"), items[0]); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Interrupted1_NoItems(string newline, bool trickle, bool useAsync) + { + _ = newline; + + using Stream stream = GetStream([0xEF], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Interrupted2_NoItems(string newline, bool trickle, bool useAsync) + { + _ = newline; + + using Stream stream = GetStream([0xEF, 0xBB], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Bom_Interrupted3_NoItems(string newline, bool trickle, bool useAsync) + { + _ = newline; + + using Stream stream = GetStream([0xEF, 0xBB, 0xBF], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(0, items.Count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task LongLines_ItemsProducedCorrectly(string newline, bool trickle, bool useAsync) + { + string[] expected = Enumerable.Range(1, 100).Select(i => string.Concat(Enumerable.Repeat($"{i} ", i))).ToArray(); + + using Stream stream = GetStream([..expected.Select(s => $"data: {s}{newline}{newline}").SelectMany(Encoding.UTF8.GetBytes)], trickle); + + List> items = useAsync ? + await ReadAllEventsAsync(stream) : + ReadAllEvents(stream); + + Assert.Equal(expected.Length, items.Count); + for (int i = 0; i < expected.Length; i++) + { + AssertSseItemEqual(new SseItem(expected[i], "message"), items[i]); + } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task Delegate_ThrowsException_Propagates(string newline, bool trickle, bool useAsync) + { + using Stream stream = GetStream($"data: hello{newline}{newline}data:world{newline}{newline}", trickle); + + SseParser parser = SseParser.Create(stream, (eventType, bytes) => throw new FormatException(Encoding.UTF8.GetString(bytes.ToArray()))); + + FormatException fe; + if (useAsync) + { + await using IAsyncEnumerator> e = parser.EnumerateAsync().GetAsyncEnumerator(); + fe = await Assert.ThrowsAsync(async () => await e.MoveNextAsync()); + } + else + { + using IEnumerator> e = parser.Enumerate().GetEnumerator(); + fe = Assert.Throws(() => e.MoveNext()); + } + + Assert.Equal("hello", fe.Message); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Cancellation_Propagates(bool cancelEnumerator) + { + using Stream stream = GetStream($"data: hello\n\ndata:world\n\n", trickle: true); + + SseParser parser = SseParser.Create(stream); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + await using IAsyncEnumerator> e = cancelEnumerator ? + parser.EnumerateAsync().GetAsyncEnumerator(cts.Token) : + parser.EnumerateAsync(cts.Token).GetAsyncEnumerator(); + + await Assert.ThrowsAnyAsync(async () => await e.MoveNextAsync()); + } + + [Fact] + public void NonGenericEnumerator_ProducesExpectedItems() + { + using Stream stream = GetStream($"data: hello\n\ndata:world\n\n", trickle: false); + + IEnumerable sse = SseParser.Create(stream).Enumerate(); + IEnumerator e = sse.GetEnumerator(); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("hello", "message"), (SseItem)e.Current); + + Assert.True(e.MoveNext()); + AssertSseItemEqual(new SseItem("world", "message"), (SseItem)e.Current); + + Assert.False(e.MoveNext()); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task MultipleItemParsers_OpenAI_StreamingResponse(string newline, bool trickle, bool useAsync) + { + string exampleResponse = + $"data: {{\"id\":\"xxx\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{\"content\":\"!\"}},\"index\":0,\"finish_reason\":null}}]}}{newline}{newline}" + + $"data: {{\"id\":\"yyy\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{}},\"index\":0,\"finish_reason\":\"stop\"}}]}}{newline}{newline}" + + $"data: [DONE]{newline}{newline}"; + + using Stream stream = GetStream(exampleResponse, trickle); + + SseItemParser itemParser = (eventType, bytes) => + { + return bytes.SequenceEqual("[DONE]"u8) ? + new ChunkOrDone { Done = true } : + new ChunkOrDone { Json = JsonSerializer.Deserialize(bytes) }; + }; + + List> items = useAsync ? + await ReadAllEventsAsync(stream, itemParser) : + ReadAllEvents(stream, itemParser); + + Assert.Equal(3, items.Count); + Assert.False(items[0].Data.Done); + Assert.False(items[1].Data.Done); + Assert.True(items[2].Data.Done); + } + + private struct ChunkOrDone + { + public JsonElement Json { get; set; } + public bool Done { get; set; } + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task ArrayPoolRental_PerItem(string newline, bool trickle, bool useAsync) + { + string exampleResponse = + $"data: {{\"id\":\"xxx\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{\"content\":\"!\"}},\"index\":0,\"finish_reason\":null}}]}}{newline}{newline}" + + $"data: {{\"id\":\"yyy\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{}},\"index\":0,\"finish_reason\":\"stop\"}}]}}{newline}{newline}" + + $"data: [DONE]{newline}{newline}"; + + using Stream stream = GetStream(exampleResponse, trickle); + + SseItemParser> itemParser = (eventType, bytes) => + { + byte[] array = ArrayPool.Shared.Rent(bytes.Length); + bytes.CopyTo(array.AsSpan()); + return new ArraySegment(array, 0, bytes.Length); + }; + + int count = 0; + if (useAsync) + { + foreach (var e in SseParser.Create(stream, itemParser).Enumerate()) + { + try + { + if ("[DONE]"u8.SequenceEqual(e.Data)) + { + break; + } + } + finally + { + ArrayPool.Shared.Return(e.Data.Array); + } + + count++; + } + } + else + { + await foreach (var e in SseParser.Create(stream, itemParser).EnumerateAsync()) + { + try + { + if ("[DONE]"u8.SequenceEqual(e.Data)) + { + break; + } + } + finally + { + ArrayPool.Shared.Return(e.Data.Array); + } + + count++; + } + } + + Assert.Equal(2, count); + } + + [Theory] + [MemberData(nameof(NewlineTrickleAsyncData))] + public async Task ArrayPoolRental_Closure(string newline, bool trickle, bool useAsync) + { + string exampleResponse = + $"data: {{\"id\":\"xxx\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{\"content\":\"!\"}},\"index\":0,\"finish_reason\":null}}]}}{newline}{newline}" + + $"data: {{\"id\":\"yyy\",\"object\":\"chat.completion.chunk\",\"created\":1679168243,\"model\":\"mmm\",\"choices\":[{{\"delta\":{{}},\"index\":0,\"finish_reason\":\"stop\"}}]}}{newline}{newline}" + + $"data: [DONE]{newline}{newline}"; + + using Stream stream = GetStream(exampleResponse, trickle); + + byte[] arrayPoolArray = ArrayPool.Shared.Rent(1); + + SseItemParser> itemParser = (eventType, bytes) => + { + if (arrayPoolArray.Length < bytes.Length) + { + byte[] temp = arrayPoolArray; + arrayPoolArray = ArrayPool.Shared.Rent(bytes.Length); + ArrayPool.Shared.Return(temp); + } + bytes.CopyTo(arrayPoolArray.AsSpan()); + return new ReadOnlyMemory(arrayPoolArray, 0, bytes.Length); + }; + + int count = 0; + if (useAsync) + { + foreach (var e in SseParser.Create(stream, itemParser).Enumerate()) + { + if ("[DONE]"u8.SequenceEqual(e.Data.Span)) + { + break; + } + count++; + } + } + else + { + await foreach (var e in SseParser.Create(stream, itemParser).EnumerateAsync()) + { + if ("[DONE]"u8.SequenceEqual(e.Data.Span)) + { + break; + } + count++; + } + } + + ArrayPool.Shared.Return(arrayPoolArray); + + Assert.Equal(2, count); + } + + private static void AssertSseItemEqual(SseItem left, SseItem right) + { + Assert.Equal(left.EventType, right.EventType); + if (left.Data is string leftData && right.Data is string rightData) + { + Assert.Equal($"{leftData.Length} {leftData}", $"{rightData.Length} {rightData}"); + } + else + { + Assert.Equal(left.Data, right.Data); + } + } + + public static IEnumerable NewlineTrickleAsyncData() => + from newline in new[] { "\r", "\n", "\r\n" } + from trickle in new[] { false, true } + from async in new[] { false, true } + select new object[] { newline, trickle, async }; + + private static Stream GetStream(string data, bool trickle) => + GetStream(Encoding.UTF8.GetBytes(data), trickle); + + private static Stream GetStream(byte[] bytes, bool trickle) => + trickle ? new TrickleStream(bytes) : new MemoryStream(bytes); + + private static List> ReadAllEvents(Stream stream) + { + return new List>(SseParser.Create(stream).Enumerate()); + } + + private static List> ReadAllEvents(Stream stream, SseItemParser parser) + { + return new List>(SseParser.Create(stream, parser).Enumerate()); + } + + private static async Task>> ReadAllEventsAsync(Stream stream, SseItemParser parser) + { + var list = new List>(); + await foreach (SseItem item in SseParser.Create(stream, parser).EnumerateAsync()) + { + list.Add(item); + } + + return list; + } + + private static async Task>> ReadAllEventsAsync(Stream stream) + { + var list = new List>(); + await foreach (SseItem item in SseParser.Create(stream).EnumerateAsync()) + { + list.Add(item); + } + + return list; + } + + /// Stream where each read reads at most one byte and where every asynchronous operation yields. + private sealed class TrickleStream : MemoryStream + { + public TrickleStream(byte[] buffer) : base(buffer) { } + + public override int Read(byte[] buffer, int offset, int count) => + base.Read(buffer, offset, Math.Min(count, 1)); + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await Task.Yield(); + return await base.ReadAsync(buffer, offset, Math.Min(count, 1), cancellationToken); + } + +#if NET + public override int Read(Span buffer) => + base.Read(buffer.Slice(0, Math.Min(buffer.Length, 1))); + + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + await Task.Yield(); + return await base.ReadAsync(buffer.Slice(0, Math.Min(buffer.Length, 1)), cancellationToken); + } +#endif + } + } +} diff --git a/src/libraries/System.Net.ServerSentEvents/tests/System.Net.ServerSentEvents.Tests.csproj b/src/libraries/System.Net.ServerSentEvents/tests/System.Net.ServerSentEvents.Tests.csproj new file mode 100644 index 0000000000000..e2b3d73610d48 --- /dev/null +++ b/src/libraries/System.Net.ServerSentEvents/tests/System.Net.ServerSentEvents.Tests.csproj @@ -0,0 +1,19 @@ + + + + $(NetCoreAppCurrent);$(NetFrameworkMinimum) + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index 646c71d629948..c8df9f89c4a03 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -636,7 +636,7 @@ public bool DontFragment { get { - if (_addressFamily == AddressFamily.InterNetwork) + if (_addressFamily == AddressFamily.InterNetwork || (_addressFamily == AddressFamily.InterNetworkV6 && DualMode)) { return (int)GetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment)! != 0 ? true : false; } @@ -648,7 +648,7 @@ public bool DontFragment set { - if (_addressFamily == AddressFamily.InterNetwork) + if (_addressFamily == AddressFamily.InterNetwork || (_addressFamily == AddressFamily.InterNetworkV6 && DualMode)) { SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, value ? 1 : 0); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 4ca023232b1a5..dd50676ac55c4 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -676,11 +676,6 @@ public override unsafe void InvokeCallback(bool allowPooling) SocketError ec = ErrorCode; Memory buffer = Buffer; - if (allowPooling) - { - AssociatedContext.ReturnOperation(this); - } - if (buffer.Length == 0) { // Invoke callback only when we are completely done. diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs index aafcf158518a9..f14c6753e93d7 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs @@ -87,12 +87,23 @@ private static SocketAsyncEngine[] CreateEngines() // private readonly ConcurrentQueue _eventQueue = new ConcurrentQueue(); + // The scheme works as follows: + // - From NotScheduled, the only transition is to Scheduled when new events are enqueued and a work item is enqueued to process them. + // - From Scheduled, the only transition is to Determining right before trying to dequeue an event. + // - From Determining, it can go to either NotScheduled when no events are present in the queue (the previous work item processed all of them) + // or Scheduled if the queue is still not empty (let the current work item handle parallelization as convinient). // - // This field is set to 1 to indicate that a thread pool work item is scheduled to process events in _eventQueue. It is - // set to 0 when the scheduled work item starts running, to indicate that a thread pool work item to process events is - // not scheduled. Changes are protected by atomic operations as appropriate. - // - private int _eventQueueProcessingRequested; + // The goal is to avoid enqueueing more work items than necessary, while still ensuring that all events are processed. + // Another work item isn't enqueued to the thread pool hastily while the state is Determining, + // instead the parallelizer takes care of that. We also ensure that only one thread can be parallelizing at any time. + private enum EventQueueProcessingStage + { + NotScheduled, + Determining, + Scheduled + } + + private int _eventQueueProcessingStage; // // Registers the Socket with a SocketAsyncEngine, and returns the associated engine. @@ -190,9 +201,14 @@ private void EventLoop() // The native shim is responsible for ensuring this condition. Debug.Assert(numEvents > 0, $"Unexpected numEvents: {numEvents}"); - if (handler.HandleSocketEvents(numEvents)) + // Only enqueue a work item if the stage is NotScheduled. + // Otherwise there must be a work item already queued or another thread already handling parallelization. + if (handler.HandleSocketEvents(numEvents) && + Interlocked.Exchange( + ref _eventQueueProcessingStage, + (int)EventQueueProcessingStage.Scheduled) == (int)EventQueueProcessingStage.NotScheduled) { - ScheduleToProcessEvents(); + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); } } } @@ -202,42 +218,73 @@ private void EventLoop() } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ScheduleToProcessEvents() + private void UpdateEventQueueProcessingStage(bool isEventQueueEmpty) { - // Schedule a thread pool work item to process events. Only one work item is scheduled at any given time to avoid - // over-parallelization. When the work item begins running, this field is reset to 0, allowing for another work item - // to be scheduled for parallelizing processing of events. - if (Interlocked.CompareExchange(ref _eventQueueProcessingRequested, 1, 0) == 0) + if (!isEventQueueEmpty) { - ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); + // There are more events to process, set stage to Scheduled and enqueue a work item. + _eventQueueProcessingStage = (int)EventQueueProcessingStage.Scheduled; + } + else + { + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have scheduled a work item to process the work, so schedule one now. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref _eventQueueProcessingStage, + (int)EventQueueProcessingStage.NotScheduled, + (int)EventQueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)EventQueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)EventQueueProcessingStage.Determining) + { + return; + } } + + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); } void IThreadPoolWorkItem.Execute() { - // Indicate that a work item is no longer scheduled to process events. The change needs to be visible to enqueuer - // threads (only for EventLoop() currently) before an event is attempted to be dequeued. In particular, if an - // enqueuer queues an event and does not schedule a work item because it is already scheduled, and this thread is - // the last thread processing events, it must see the event queued by the enqueuer. - Interlocked.Exchange(ref _eventQueueProcessingRequested, 0); - ConcurrentQueue eventQueue = _eventQueue; - if (!eventQueue.TryDequeue(out SocketIOEvent ev)) + SocketIOEvent ev; + while (true) { - return; - } + Debug.Assert(_eventQueueProcessingStage == (int)EventQueueProcessingStage.Scheduled); - int startTimeMs = Environment.TickCount; + // The change needs to be visible to other threads that may request a worker thread before a work item is attempted + // to be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not request a + // thread because it sees a Determining or Scheduled stage, and the current thread is the last thread processing + // work items, the current thread must either see the work item queued by the enqueuer, or it must see a stage of + // Scheduled, and try to dequeue again or request another thread. + _eventQueueProcessingStage = (int)EventQueueProcessingStage.Determining; + Interlocked.MemoryBarrier(); + + if (eventQueue.TryDequeue(out ev)) + { + break; + } - // An event was successfully dequeued, and there may be more events to process. Schedule a work item to parallelize - // processing of events, before processing more events. Following this, it is the responsibility of the new work - // item and the epoll thread to schedule more work items as necessary. The parallelization may be necessary here if - // the user callback as part of handling the event blocks for some reason that may have a dependency on other queued - // socket events. - ScheduleToProcessEvents(); + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have scheduled a work item to process the work, so try to dequeue a work item again. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref _eventQueueProcessingStage, + (int)EventQueueProcessingStage.NotScheduled, + (int)EventQueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)EventQueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)EventQueueProcessingStage.Determining) + { + return; + } + } - while (true) + UpdateEventQueueProcessingStage(eventQueue.IsEmpty); + + int startTimeMs = Environment.TickCount; + do { ev.Context.HandleEvents(ev.Events); @@ -253,19 +300,7 @@ void IThreadPoolWorkItem.Execute() // using Stopwatch instead (like 1 ms, 5 ms, etc.), from quick tests they appeared to have a slightly greater // impact on throughput compared to the threshold chosen below, though it is slight enough that it may not // matter much. Higher thresholds didn't seem to have any noticeable effect. - if (Environment.TickCount - startTimeMs >= 15) - { - break; - } - - if (!eventQueue.TryDequeue(out ev)) - { - return; - } - } - - // The queue was not observed to be empty, schedule another work item before yielding the thread - ScheduleToProcessEvents(); + } while (Environment.TickCount - startTimeMs < 15 && eventQueue.TryDequeue(out ev)); } private void FreeNativeResources() diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs index 57c4d32a9738a..819c3bc111027 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs @@ -392,9 +392,9 @@ public sealed class AcceptApm : Accept public AcceptApm(ITestOutputHelper output) : base(output) {} [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void AbortedByDispose_LeaksNoUnobservedExceptions() + public async Task AbortedByDispose_LeaksNoUnobservedExceptions() { - RemoteExecutor.Invoke(static async () => + await RemoteExecutor.Invoke(static async () => { var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.BindToAnonymousPort(IPAddress.Loopback); @@ -431,7 +431,7 @@ await Task.Run(() => GC.WaitForPendingFinalizers(); Assert.False(unobservedThrown); - }).Dispose(); + }).DisposeAsync(); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index f0f7ed672ac04..3c81a04cedd3d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -1157,5 +1157,16 @@ public void CancelConnectAsync_NullEventArgs_Throws_ArgumentNull() { Assert.Throws(() => Socket.CancelConnectAsync(null)); } + + // MacOS And FreeBSD do not support setting don't-fragment (DF) bit on dual mode socket + [Fact] + [PlatformSpecific(TestPlatforms.Linux | TestPlatforms.Windows)] + public void CanSetDontFragment_OnIPV6Address_DualModeSocket() + { + using Socket socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + socket.DualMode = true; + socket.DontFragment = true; + Assert.True(socket.DontFragment); + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs index 4cc267e0113eb..52db82ab7c399 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs @@ -131,14 +131,14 @@ public void Ctor_Raw_NotSupported_ExpectedError(AddressFamily addressFamily, Pro [InlineData(false, 1)] [InlineData(true, 2)] // Begin/EndAccept [InlineData(false, 2)] - public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter) + public async Task CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter) { // 300 ms should be long enough to connect if the socket is actually present & listening. const int ConnectionTimeoutMs = 300; // Run the test in another process so as to not have trouble with other tests // launching child processes that might impact inheritance. - RemoteExecutor.Invoke((validateClientString, acceptApiString) => + await RemoteExecutor.Invoke((validateClientString, acceptApiString) => { bool validateClient = bool.Parse(validateClientString); int acceptApi = int.Parse(acceptApiString); @@ -211,7 +211,7 @@ public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOu } } } - }, validateClientOuter.ToString(), acceptApiOuter.ToString()).Dispose(); + }, validateClientOuter.ToString(), acceptApiOuter.ToString()).DisposeAsync(); } [Theory] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs index 6c5c2d8e2a9ce..17c5a3e18240b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs @@ -1303,7 +1303,7 @@ private void ReceiveMessageFrom_Helper(IPAddress listenOn, IPAddress connectTo, Assert.Throws(() => { // This is a false start. - // https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receivemessagefrom + // https://learn.microsoft.com/dotnet/api/system.net.sockets.socket.receivemessagefrom // "...the returned IPPacketInformation object will only be valid for packets which arrive at the // local computer after the socket option has been set. If a socket is sent packets between when // it is bound to a local endpoint (explicitly by the Bind method or implicitly by one of the Connect, diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/InlineCompletions.Unix.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/InlineCompletions.Unix.cs index 3b8f9bfc8927d..511b8b0dabe5e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/InlineCompletions.Unix.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/InlineCompletions.Unix.cs @@ -15,13 +15,13 @@ public class InlineContinuations [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [PlatformSpecific(TestPlatforms.AnyUnix)] // Inline Socket mode is specific to Unix Socket implementation. - public void InlineSocketContinuations() + public async Task InlineSocketContinuations() { RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables.Add("DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS", "1"); options.TimeOut = (int)TimeSpan.FromMinutes(20).TotalMilliseconds; - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { // Connect/Accept tests await new AcceptEap(null).Accept_ConcurrentAcceptsBeforeConnects_Success(5); @@ -33,7 +33,7 @@ public void InlineSocketContinuations() await new SendReceive_Eap(null).SendRecv_Stream_TCP_MultipleConcurrentSends(IPAddress.Loopback, useMultipleBuffers: false); await new SendReceive_Eap(null).TcpReceiveSendGetsCanceledByDispose(receiveOrSend: true, ipv6Server: false, dualModeClient: false, owning: true); await new SendReceive_Eap(null).TcpReceiveSendGetsCanceledByDispose(receiveOrSend: false, ipv6Server: false, dualModeClient: false, owning: true); - }, options).Dispose(); + }, options).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs index 134c80d9cf3e1..d23ee70a0b4a8 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Diagnostics.Tracing; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; using Xunit.Abstractions; @@ -32,9 +33,9 @@ public static void EventSource_ExistsWithCorrectId() [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_EventsRaisedAsExpected() + public async Task EventSource_EventsRaisedAsExpected() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.Sockets", EventLevel.Verbose)) { @@ -61,7 +62,7 @@ await listener.RunWithCallbackAsync(events.Enqueue, async () => Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself Assert.InRange(events.Count, 1, int.MaxValue); } - }).Dispose(); + }).DisposeAsync(); } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs index e103fde492b40..f7427e88da001 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs @@ -3,6 +3,7 @@ using System.Threading; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -27,11 +28,11 @@ public void SupportsIPv6_MatchesOSSupportsIPv6() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DisableIPv6_OSSupportsIPv6_False() + public async Task DisableIPv6_OSSupportsIPv6_False() { RemoteInvokeOptions options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_DISABLEIPV6"] = "1"; - RemoteExecutor.Invoke(RunTest, options).Dispose(); + await RemoteExecutor.Invoke(RunTest, options).DisposeAsync(); static void RunTest() { @@ -40,9 +41,9 @@ static void RunTest() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DisableIPv6_SocketConstructor_CreatesIPv4Socket() + public async Task DisableIPv6_SocketConstructor_CreatesIPv4Socket() { - RemoteExecutor.Invoke(RunTest).Dispose(); + await RemoteExecutor.Invoke(RunTest).DisposeAsync(); static void RunTest() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs index 0a65addc5c3f8..209f91b2ffba9 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs @@ -1187,9 +1187,9 @@ public SendReceive_Sync(ITestOutputHelper output) : base(output) { } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void BlockingRead_DoesntRequireAnotherThreadPoolThread() + public async Task BlockingRead_DoesntRequireAnotherThreadPoolThread() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { // Set the max number of worker threads to a low value. ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); @@ -1232,7 +1232,7 @@ select Task.Factory.StartNew(() => pair.Item1.Receive(new byte[1]), Cancellation pair.Item2.Dispose(); } } - }).Dispose(); + }).DisposeAsync(); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs index 59f0dd8b5fa4c..afd56dfcc9ef7 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs @@ -1098,10 +1098,11 @@ internal static void Connect(this Socket socket, EndPoint ep, Memory buffe saea.SetBuffer(buffer); saea.RemoteEndPoint = ep; saea.Completed += (_, __) => re.Set(); - if (!socket.ConnectAsync(saea)) + if (socket.ConnectAsync(saea)) { re.Wait(timeout); } + Assert.True(socket.Connected); } internal static Task ConnectAsync(this Socket socket, EndPoint ep, Memory buffer, CancellationToken cancellationToken = default) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index b59c5ebd92a33..a9cb168de3ecc 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -161,7 +161,7 @@ public async Task DuplicateAndClose_TcpListener() [PlatformSpecific(TestPlatforms.Windows)] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void DuplicateSocket_IsNotInheritable() + public async Task DuplicateSocket_IsNotInheritable() { // 300 ms should be long enough to connect if the socket is actually present & listening. const int ConnectionTimeoutMs = 300; @@ -204,7 +204,7 @@ static void ChildProcessBody(string clientPipeHandle) // Run the test in another process so as to not have trouble with other tests // launching child processes that might impact inheritance. - RemoteExecutor.Invoke(RunTest).Dispose(); + await RemoteExecutor.Invoke(RunTest).DisposeAsync(); } [Fact] @@ -324,17 +324,20 @@ public async Task DuplicateAndClose_TcpServerHandler(AddressFamily addressFamily if (sameProcess) { Task handlerCode = Task.Run(() => HandlerServerCode(_ipcPipeName)); - RunCommonHostLogic(Environment.ProcessId); + await RunCommonHostLogic(Environment.ProcessId); await handlerCode; } else { - using RemoteInvokeHandle hServerProc = RemoteExecutor.Invoke(HandlerServerCode, _ipcPipeName); - RunCommonHostLogic(hServerProc.Process.Id); + RemoteInvokeHandle hServerProc = RemoteExecutor.Invoke(HandlerServerCode, _ipcPipeName); + await RunCommonHostLogic(hServerProc.Process.Id); + await hServerProc.DisposeAsync(); } - void RunCommonHostLogic(int processId) + async Task RunCommonHostLogic(int processId) { + await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + pipeServerStream.WaitForConnection(); // Duplicate the socket: diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/StartupTests.Windows.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/StartupTests.Windows.cs index 4666ea381ce8f..1f9c39daa0917 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/StartupTests.Windows.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/StartupTests.Windows.cs @@ -3,6 +3,7 @@ using System.IO.Pipes; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; @@ -16,82 +17,82 @@ public class StartupTests // RemoteExecutor is used so that the individual method is used as early in the process as possible. [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void OSSupportsIPv4() + public static async Task OSSupportsIPv4() { bool parentSupported = Socket.OSSupportsIPv4; - RemoteExecutor.Invoke(parentSupported => + await RemoteExecutor.Invoke(parentSupported => { Assert.Equal(bool.Parse(parentSupported), Socket.OSSupportsIPv4); - }, parentSupported.ToString()).Dispose(); + }, parentSupported.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void OSSupportsIPv6() + public static async Task OSSupportsIPv6() { bool parentSupported = Socket.OSSupportsIPv6; - RemoteExecutor.Invoke(parentSupported => + await RemoteExecutor.Invoke(parentSupported => { Assert.Equal(bool.Parse(parentSupported), Socket.OSSupportsIPv6); - }, parentSupported.ToString()).Dispose(); + }, parentSupported.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void OSSupportsUnixDomainSockets() + public static async Task OSSupportsUnixDomainSockets() { bool parentSupported = Socket.OSSupportsUnixDomainSockets; - RemoteExecutor.Invoke(parentSupported => + await RemoteExecutor.Invoke(parentSupported => { Assert.Equal(bool.Parse(parentSupported), Socket.OSSupportsUnixDomainSockets); - }, parentSupported.ToString()).Dispose(); + }, parentSupported.ToString()).DisposeAsync(); } #pragma warning disable CS0618 // SupportsIPv4 and SupportsIPv6 are obsolete [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void SupportsIPv4() + public static async Task SupportsIPv4() { bool parentSupported = Socket.SupportsIPv4; - RemoteExecutor.Invoke(parentSupported => + await RemoteExecutor.Invoke(parentSupported => { Assert.Equal(bool.Parse(parentSupported), Socket.SupportsIPv4); - }, parentSupported.ToString()).Dispose(); + }, parentSupported.ToString()).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void SupportsIPv6() + public static async Task SupportsIPv6() { bool parentSupported = Socket.SupportsIPv6; - RemoteExecutor.Invoke(parentSupported => + await RemoteExecutor.Invoke(parentSupported => { Assert.Equal(bool.Parse(parentSupported), Socket.SupportsIPv6); - }, parentSupported.ToString()).Dispose(); + }, parentSupported.ToString()).DisposeAsync(); } #pragma warning restore CS0618 [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Ctor_SocketType_ProtocolType() + public static async Task Ctor_SocketType_ProtocolType() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { new Socket(SocketType.Stream, ProtocolType.Tcp).Dispose(); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Ctor_AddressFamily_SocketType_ProtocolType() + public static async Task Ctor_AddressFamily_SocketType_ProtocolType() { - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp).Dispose(); - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Ctor_SafeHandle() => RemoteExecutor.Invoke(() => + public static async Task Ctor_SafeHandle() => await RemoteExecutor.Invoke(() => { using var pipe = new AnonymousPipeServerStream(); using SafeHandle clientSafeHandle = pipe.ClientSafePipeHandle; SocketException se = Assert.Throws(() => new Socket(new SafeSocketHandle(clientSafeHandle.DangerousGetHandle(), ownsHandle: false))); Assert.Equal(SocketError.NotSocket, se.SocketErrorCode); - }).Dispose(); + }).DisposeAsync(); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index a1367f4a0b220..cc57803fa1c50 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -86,6 +86,8 @@ Link="Common\System\Net\EventSourceTestLogging.cs" /> + + await RemoteExecutor.Invoke(async (connectMethod, acceptMethod) => { using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1); listener.AddActivityTracking(); @@ -149,7 +149,7 @@ await listener.RunWithCallbackAsync(e => VerifyEvents(events, connect: true, expectedCount: 1); VerifyEvents(events, connect: false, expectedCount: 1); VerifyEventCounters(events, connectCount: 1, hasCurrentConnectCounter: true); - }, connectMethod, acceptMethod).Dispose(); + }, connectMethod, acceptMethod).DisposeAsync(); } [OuterLoop] @@ -162,7 +162,7 @@ public async Task EventSource_SocketConnectsRemote_LogsConnectStartStop(string c throw new SkipTestException("The remote server is not reachable"); } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => + await RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1); listener.AddActivityTracking(); @@ -184,14 +184,14 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn VerifyEvents(events, connect: true, expectedCount: 1); VerifyEventCounters(events, connectCount: 1, connectOnly: true); - }, connectMethod, useDnsEndPoint.ToString()).Dispose(); + }, connectMethod, useDnsEndPoint.ToString()).DisposeAsync(); } [OuterLoop] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Same as Connect.ConnectGetsCanceledByDispose")] [MemberData(nameof(SocketMethods_WithBools_MemberData))] - public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) + public async Task EventSource_SocketConnectFailure_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { // Skip test on Linux kernels that may have a regression that was fixed in 6.6. // See TcpReceiveSendGetsCanceledByDispose test for additional information. @@ -200,7 +200,7 @@ public void EventSource_SocketConnectFailure_LogsConnectFailed(string connectMet return; } - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => + await RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -236,15 +236,15 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); VerifyEventCounters(events, connectCount: 0); - }, connectMethod, useDnsEndPoint.ToString()).Dispose(); + }, connectMethod, useDnsEndPoint.ToString()).DisposeAsync(); } [OuterLoop] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(SocketMethods_MemberData))] - public void EventSource_SocketAcceptFailure_LogsAcceptFailed(string acceptMethod) + public async Task EventSource_SocketAcceptFailure_LogsAcceptFailed(string acceptMethod) { - RemoteExecutor.Invoke(async acceptMethod => + await RemoteExecutor.Invoke(async acceptMethod => { using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1); listener.AddActivityTracking(); @@ -271,7 +271,7 @@ await Assert.ThrowsAnyAsync(async () => VerifyEvents(events, connect: false, expectedCount: 1, shouldHaveFailures: true); VerifyEventCounters(events, connectCount: 0); - }, acceptMethod).Dispose(); + }, acceptMethod).DisposeAsync(); } [OuterLoop] @@ -280,9 +280,9 @@ await Assert.ThrowsAnyAsync(async () => [InlineData("Task", false)] [InlineData("Eap", true)] [InlineData("Eap", false)] - public void EventSource_ConnectAsyncCanceled_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) + public async Task EventSource_ConnectAsyncCanceled_LogsConnectFailed(string connectMethod, bool useDnsEndPoint) { - RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => + await RemoteExecutor.Invoke(async (connectMethod, useDnsEndPointString) => { EndPoint endPoint = await GetRemoteEndPointAsync(useDnsEndPointString, port: 12345); @@ -336,14 +336,14 @@ await Assert.ThrowsAnyAsync(async () => int? expectedCount = bool.Parse(useDnsEndPointString) ? null : 1; VerifyEvents(events, connect: true, expectedCount, shouldHaveFailures: true); VerifyEventCounters(events, connectCount: 0); - }, connectMethod, useDnsEndPoint.ToString()).Dispose(); + }, connectMethod, useDnsEndPoint.ToString()).DisposeAsync(); } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void EventSource_EventsRaisedAsExpected() + public async Task EventSource_EventsRaisedAsExpected() { - RemoteExecutor.Invoke(async () => + await RemoteExecutor.Invoke(async () => { using (var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1)) { @@ -378,7 +378,7 @@ await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), asyn VerifyEvents(events, connect: true, expectedCount: 10); VerifyEventCounters(events, connectCount: 10, shouldHaveTransferredBytes: true, shouldHaveDatagrams: true); } - }).Dispose(); + }).DisposeAsync(); } private static async Task WaitForEventAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string name) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index a3e8501348375..58edc1ecffaa5 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -467,13 +467,13 @@ public void Socket_CreateUnixDomainSocket_Throws_OnWindows() [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] - public void UnixDomainSocketEndPoint_RelativePathDeletesFile() + public async Task UnixDomainSocketEndPoint_RelativePathDeletesFile() { if (!Socket.OSSupportsUnixDomainSockets) { return; } - RemoteExecutor.Invoke(() => + await RemoteExecutor.Invoke(() => { using (Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { @@ -501,7 +501,7 @@ public void UnixDomainSocketEndPoint_RelativePathDeletesFile() Directory.Delete(otherDir); } } - }).Dispose(); + }).DisposeAsync(); } [ConditionalFact(typeof(Socket), nameof(Socket.OSSupportsUnixDomainSockets))] diff --git a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml index a072803b8d855..2ce75e7b71da8 100644 --- a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md index c5670c1c0f989..4fcfa9bff7241 100644 --- a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md +++ b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md @@ -46,7 +46,7 @@ The main types provided by this library are: ## Additional Documentation -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/system.numerics.tensors) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.numerics.tensors) ## Feedback & Contributing diff --git a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ManualShimTypeForwards.cs b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ManualShimTypeForwards.cs index ab8473216cd87..2fd89ceeb3259 100644 --- a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ManualShimTypeForwards.cs +++ b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ManualShimTypeForwards.cs @@ -6,24 +6,30 @@ namespace System { - public sealed partial class CultureAwareComparer : System.StringComparer, System.Runtime.Serialization.ISerializable + public sealed partial class CultureAwareComparer : System.StringComparer, System.Collections.Generic.IAlternateEqualityComparer, string?>, System.Runtime.Serialization.ISerializable { internal CultureAwareComparer() { } public override int Compare(string? x, string? y) { throw null; } + string System.Collections.Generic.IAlternateEqualityComparer, string?>.Create(System.ReadOnlySpan span) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override bool Equals(string? x, string? y) { throw null; } + bool System.Collections.Generic.IAlternateEqualityComparer, string?>.Equals(System.ReadOnlySpan span, string? target) { throw null; } public override int GetHashCode() { throw null; } public override int GetHashCode(string obj) { throw null; } + int System.Collections.Generic.IAlternateEqualityComparer, string?>.GetHashCode(System.ReadOnlySpan span) { throw null; } public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public partial class OrdinalComparer : System.StringComparer + public partial class OrdinalComparer : System.StringComparer, System.Collections.Generic.IAlternateEqualityComparer, string?> { internal OrdinalComparer() { } public override int Compare(string? x, string? y) { throw null; } + string System.Collections.Generic.IAlternateEqualityComparer, string?>.Create(System.ReadOnlySpan span) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override bool Equals(string? x, string? y) { throw null; } + bool System.Collections.Generic.IAlternateEqualityComparer, string?>.Equals(System.ReadOnlySpan span, string? target) { throw null; } public override int GetHashCode() { throw null; } public override int GetHashCode(string obj) { throw null; } + int System.Collections.Generic.IAlternateEqualityComparer, string?>.GetHashCode(System.ReadOnlySpan span) { throw null; } } [System.ObsoleteAttribute("Formatter-based serialization is obsolete and should not be used.", DiagnosticId = "SYSLIB0050", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public sealed partial class UnitySerializationHolder : System.Runtime.Serialization.IObjectReference, System.Runtime.Serialization.ISerializable diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 4a27078217114..b9d10848b5a3d 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -235,6 +235,9 @@ Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: '{0}'. + + The collection's comparer does not support the requested operation. + Not enough space available in the buffer. 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 3ec13eb3791f8..ea91a44b9fcec 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 @@ -190,6 +190,7 @@ + @@ -2785,4 +2786,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Action.cs b/src/libraries/System.Private.CoreLib/src/System/Action.cs index dacc4fa7cc5b7..9c78bed630e03 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Action.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Action.cs @@ -6,37 +6,196 @@ namespace System { public delegate void Action(); - public delegate void Action(T obj); - public delegate void Action(T1 arg1, T2 arg2); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); + + public delegate void Action(T obj) + where T : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2) + where T1 : allows ref struct + where T2 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where T16 : allows ref struct; // This should probably technically be T? rather than T to match `IComparer`. However, the use cases are generally different, // with Comparison typically being used via a lambda, with the T inferred from the type of the collection being sorted, // and forcing nullable warnings onto all such usage leads to many spurious warnings. - public delegate int Comparison(T x, T y); + public delegate int Comparison(T x, T y) + where T : allows ref struct; - public delegate TOutput Converter(TInput input); + public delegate TOutput Converter(TInput input) + where TInput : allows ref struct + where TOutput : allows ref struct; - public delegate bool Predicate(T obj); + public delegate bool Predicate(T obj) + where T : allows ref struct; } namespace System.Buffers { - public delegate void SpanAction(Span span, TArg arg); - public delegate void ReadOnlySpanAction(ReadOnlySpan span, TArg arg); + public delegate void SpanAction(Span span, TArg arg) + where TArg : allows ref struct; + + public delegate void ReadOnlySpanAction(ReadOnlySpan span, TArg arg) + where TArg : allows ref struct; internal delegate TResult SpanFunc(Span span, T1 arg1, T2 arg2, T3 arg3); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 4d077d8ac3f9c..b0837a535f00b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -136,8 +136,27 @@ public static partial class Activator [Intrinsic] public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>() + where T : allows ref struct { - return (T)((RuntimeType)typeof(T)).CreateInstanceOfT()!; + var rtType = (RuntimeType)typeof(T); + if (!rtType.IsValueType) + { + object o = rtType.CreateInstanceOfT()!; + + // Casting the above object to T is technically invalid because + // T can be ByRefLike (that is, ref struct). Roslyn blocks the + // cast this in function with a "CS0030: Cannot convert type 'object' to 'T'", + // which is correct. However, since we are doing the IsValueType + // check above, we know this code path will only be taken with + // reference types and therefore the below Unsafe.As<> is safe. + return Unsafe.As(ref o); + } + else + { + T t = default!; + rtType.CallDefaultStructConstructor(ref Unsafe.As(ref t)); + return t; + } } private static T CreateDefaultInstance() where T : struct => default; diff --git a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs index f9ec9353812d4..bdd6e87571275 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs @@ -392,7 +392,7 @@ public override string ToString() /// because DebuggerDisplay should be a single property access or parameterless method call, so that the debugger /// can use a fast path without using the expression evaluator. /// - /// See https://docs.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute + /// See https://learn.microsoft.com/visualstudio/debugger/using-the-debuggerdisplay-attribute /// internal int InnerExceptionCount => _innerExceptions.Length; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs index cf4256f546d59..f2d3e45286042 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs @@ -160,5 +160,127 @@ public static void CopyTo(this List list, Span destination) new ReadOnlySpan(list._items, 0, list._size).CopyTo(destination); } + + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The type of the keys used by the dictionary instance. + /// The type of the values used by the dictionary instance. + /// The alternate type of a key for performing lookups. + /// The dictionary instance. + /// The created lookup instance. + /// is null. + /// 's comparer is not compatible with . + /// + /// The supplied must be using a comparer that implements with + /// and . If it doesn't, an exception will be thrown. + /// + public static Dictionary.AlternateLookup GetAlternateLookup( + this Dictionary dictionary) + where TKey : notnull + where TAlternateKey : notnull, allows ref struct + { + ArgumentNullException.ThrowIfNull(dictionary); + + if (!Dictionary.AlternateLookup.IsCompatibleKey(dictionary)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IncompatibleComparer); + } + + return new Dictionary.AlternateLookup(dictionary); + } + + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The type of the keys used by the dictionary instance. + /// The type of the values used by the dictionary instance. + /// The alternate type of a key for performing lookups. + /// The dictionary instance. + /// The created lookup instance when the method returns true, or a default instance that should not be used if the method returns false. + /// true if a lookup could be created; otherwise, false. + /// is null. + /// + /// The supplied must be using a comparer that implements with + /// and . If it doesn't, the method will return false. + /// + public static bool TryGetAlternateLookup( + this Dictionary dictionary, + out Dictionary.AlternateLookup lookup) + where TKey : notnull + where TAlternateKey : notnull, allows ref struct + { + ArgumentNullException.ThrowIfNull(dictionary); + + if (Dictionary.AlternateLookup.IsCompatibleKey(dictionary)) + { + lookup = new Dictionary.AlternateLookup(dictionary); + return true; + } + + lookup = default; + return false; + } + + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a instead of a . + /// + /// The type of the items used by the set instance. + /// The alternate type of instance for performing lookups. + /// The set instance. + /// The created lookup instance. + /// is null. + /// 's comparer is not compatible with . + /// + /// The supplied must be using a comparer that implements with + /// and . If it doesn't, an exception will be thrown. + /// + public static HashSet.AlternateLookup GetAlternateLookup( + this HashSet set) + where TAlternate : allows ref struct + { + ArgumentNullException.ThrowIfNull(set); + + if (!HashSet.AlternateLookup.IsCompatibleItem(set)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IncompatibleComparer); + } + + return new HashSet.AlternateLookup(set); + } + + /// + /// Gets an instance of a type that may be used to perform operations on a + /// using a instead of a . + /// + /// The type of the items used by the set instance. + /// The alternate type of instance for performing lookups. + /// The set instance. + /// The created lookup instance when the method returns true, or a default instance that should not be used if the method returns false. + /// true if a lookup could be created; otherwise, false. + /// is null. + /// + /// The supplied must be using a comparer that implements with + /// and . If it doesn't, the method returns false. + /// + public static bool TryGetAlternateLookup( + this HashSet set, + out HashSet.AlternateLookup lookup) + where TAlternate : allows ref struct + { + ArgumentNullException.ThrowIfNull(set); + + if (HashSet.AlternateLookup.IsCompatibleItem(set)) + { + lookup = new HashSet.AlternateLookup(set); + return true; + } + + lookup = default; + return false; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index 9b18d4a125ed2..87583ded8e455 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -636,7 +636,382 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) } /// - /// A helper class containing APIs exposed through . + /// Provides a type that may be used to perform operations on a + /// using a as a key instead of a . + /// + /// The alternate type of a key for performing lookups. + public readonly struct AlternateLookup where TAlternateKey : notnull, allows ref struct + { + /// Initialize the instance. The dictionary must have already been verified to have a compatible comparer. + internal AlternateLookup(Dictionary dictionary) + { + Debug.Assert(dictionary is not null); + Debug.Assert(IsCompatibleKey(dictionary)); + Dictionary = dictionary; + } + + /// Gets the against which this instance performs operations. + public Dictionary Dictionary { get; } + + /// Gets or sets the value associated with the specified alternate key. + /// The alternate key of the value to get or set. + /// + /// The value associated with the specified alternate key. If the specified alternate key is not found, a get operation throws + /// a , and a set operation creates a new element with the specified key. + /// + /// is . + /// The property is retrieved and alternate key does not exist in the collection. + public TValue this[TAlternateKey key] + { + get + { + ref TValue value = ref FindValue(key, out _); + if (Unsafe.IsNullRef(ref value)) + { + ThrowHelper.ThrowKeyNotFoundException(GetAlternateComparer(Dictionary).Create(key)); + } + + return value; + } + set => GetValueRefOrAddDefault(key, out _) = value; + } + + /// Checks whether the dictionary has a comparer compatible with . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsCompatibleKey(Dictionary dictionary) + { + Debug.Assert(dictionary is not null); + return dictionary._comparer is IAlternateEqualityComparer; + } + + /// Gets the dictionary's alternate comparer. The dictionary must have already been verified as compatible. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static IAlternateEqualityComparer GetAlternateComparer(Dictionary dictionary) + { + Debug.Assert(IsCompatibleKey(dictionary)); + return Unsafe.As>(dictionary._comparer); + } + + /// Gets the value associated with the specified alternate key. + /// The alternate key of the value to get. + /// + /// When this method returns, contains the value associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the value parameter. + /// + /// is . + public bool TryGetValue(TAlternateKey key, [MaybeNullWhen(false)] out TValue value) + { + ref TValue valueRef = ref FindValue(key, out _); + if (!Unsafe.IsNullRef(ref valueRef)) + { + value = valueRef; + return true; + } + + value = default; + return false; + } + + /// Gets the value associated with the specified alternate key. + /// The alternate key of the value to get. + /// + /// When this method returns, contains the actual key associated with the alternate key, if the key is found; + /// otherwise, the default value for the type of the key parameter. + /// + /// + /// When this method returns, contains the value associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the value parameter. + /// + /// is . + public bool TryGetValue(TAlternateKey key, [MaybeNullWhen(false)] out TKey actualKey, [MaybeNullWhen(false)] out TValue value) + { + ref TValue valueRef = ref FindValue(key, out actualKey); + if (!Unsafe.IsNullRef(ref valueRef)) + { + value = valueRef; + Debug.Assert(actualKey is not null); + return true; + } + + value = default; + return false; + } + + /// Determines whether the contains the specified alternate key. + /// The alternate key to check. + /// if the key is in the dictionary; otherwise, . + /// is . + public bool ContainsKey(TAlternateKey key) => + !Unsafe.IsNullRef(ref FindValue(key, out _)); + + /// Finds the entry associated with the specified alternate key. + /// The alternate key. + /// The actual key, if found. + /// A reference to the value associated with the key, if found; otherwise, a null reference. + internal ref TValue FindValue(TAlternateKey key, [MaybeNullWhen(false)] out TKey actualKey) + { + Dictionary dictionary = Dictionary; + IAlternateEqualityComparer comparer = GetAlternateComparer(dictionary); + + ref Entry entry = ref Unsafe.NullRef(); + if (dictionary._buckets != null) + { + Debug.Assert(dictionary._entries != null, "expected entries to be != null"); + + uint hashCode = (uint)comparer.GetHashCode(key); + int i = dictionary.GetBucket(hashCode); + Entry[]? entries = dictionary._entries; + uint collisionCount = 0; + i--; // Value in _buckets is 1-based; subtract 1 from i. We do it here so it fuses with the following conditional. + do + { + // Should be a while loop https://github.com/dotnet/runtime/issues/9422 + // Test in if to drop range check for following array access + if ((uint)i >= (uint)entries.Length) + { + goto ReturnNotFound; + } + + entry = ref entries[i]; + if (entry.hashCode == hashCode && comparer.Equals(key, entry.key)) + { + goto ReturnFound; + } + + i = entry.next; + + collisionCount++; + } while (collisionCount <= (uint)entries.Length); + + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + goto ConcurrentOperation; + } + + goto ReturnNotFound; + + ConcurrentOperation: + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + ReturnFound: + ref TValue value = ref entry.value; + actualKey = entry.key; + Return: + return ref value; + ReturnNotFound: + value = ref Unsafe.NullRef(); + actualKey = default!; + goto Return; + } + + /// Removes the value with the specified alternate key from the . + /// The alternate key of the element to remove. + /// true if the element is successfully found and removed; otherwise, false. + /// is . + public bool Remove(TAlternateKey key) => + Remove(key, out _, out _); + + /// + /// Removes the value with the specified alternate key from the , + /// and copies the element to the value parameter. + /// + /// The alternate key of the element to remove. + /// The removed key. + /// The removed element. + /// true if the element is successfully found and removed; otherwise, false. + /// is . + public bool Remove(TAlternateKey key, [MaybeNullWhen(false)] out TKey actualKey, [MaybeNullWhen(false)] out TValue value) + { + Dictionary dictionary = Dictionary; + IAlternateEqualityComparer comparer = GetAlternateComparer(dictionary); + + if (dictionary._buckets != null) + { + Debug.Assert(dictionary._entries != null, "entries should be non-null"); + uint collisionCount = 0; + + uint hashCode = (uint)comparer.GetHashCode(key); + + ref int bucket = ref dictionary.GetBucket(hashCode); + Entry[]? entries = dictionary._entries; + int last = -1; + int i = bucket - 1; // Value in buckets is 1-based + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.hashCode == hashCode && comparer.Equals(key, entry.key)) + { + if (last < 0) + { + bucket = entry.next + 1; // Value in buckets is 1-based + } + else + { + entries[last].next = entry.next; + } + + actualKey = entry.key; + value = entry.value; + + Debug.Assert((StartOfFreeList - dictionary._freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646"); + entry.next = StartOfFreeList - dictionary._freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default!; + } + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default!; + } + + dictionary._freeList = i; + dictionary._freeCount++; + return true; + } + + last = i; + i = entry.next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + } + + actualKey = default; + value = default; + return false; + } + + /// Attempts to add the specified key and value to the dictionary. + /// The alternate key of the element to add. + /// The value of the element to add. + /// true if the key/value pair was added to the dictionary successfully; otherwise, false. + /// is . + public bool TryAdd(TAlternateKey key, TValue value) + { + ref TValue? slot = ref GetValueRefOrAddDefault(key, out bool exists); + if (!exists) + { + slot = value; + return true; + } + + return false; + } + + /// + internal ref TValue? GetValueRefOrAddDefault(TAlternateKey key, out bool exists) + { + // NOTE: this method is a mirror of GetValueRefOrAddDefault above. Keep it in sync. + + Dictionary dictionary = Dictionary; + IAlternateEqualityComparer comparer = GetAlternateComparer(dictionary); + + if (dictionary._buckets == null) + { + dictionary.Initialize(0); + } + Debug.Assert(dictionary._buckets != null); + + Entry[]? entries = dictionary._entries; + Debug.Assert(entries != null, "expected entries to be non-null"); + + uint hashCode = (uint)comparer.GetHashCode(key); + + uint collisionCount = 0; + ref int bucket = ref dictionary.GetBucket(hashCode); + int i = bucket - 1; // Value in _buckets is 1-based + + Debug.Assert(comparer is not null); + while ((uint)i < (uint)entries.Length) + { + if (entries[i].hashCode == hashCode && comparer.Equals(key, entries[i].key)) + { + exists = true; + + return ref entries[i].value!; + } + + i = entries[i].next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + + TKey actualKey = comparer.Create(key); + if (actualKey is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + int index; + if (dictionary._freeCount > 0) + { + index = dictionary._freeList; + Debug.Assert((StartOfFreeList - entries[dictionary._freeList].next) >= -1, "shouldn't overflow because `next` cannot underflow"); + dictionary._freeList = StartOfFreeList - entries[dictionary._freeList].next; + dictionary._freeCount--; + } + else + { + int count = dictionary._count; + if (count == entries.Length) + { + dictionary.Resize(); + bucket = ref dictionary.GetBucket(hashCode); + } + index = count; + dictionary._count = count + 1; + entries = dictionary._entries; + } + + ref Entry entry = ref entries![index]; + entry.hashCode = hashCode; + entry.next = bucket - 1; // Value in _buckets is 1-based + entry.key = actualKey; + entry.value = default!; + bucket = index + 1; // Value in _buckets is 1-based + dictionary._version++; + + // Value types never rehash + if (!typeof(TKey).IsValueType && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) + { + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + dictionary.Resize(entries.Length, true); + + exists = false; + + // At this point the entries array has been resized, so the current reference we have is no longer valid. + // We're forced to do a new lookup and return an updated reference to the new entry instance. This new + // lookup is guaranteed to always find a value though and it will never return a null reference here. + ref TValue? value = ref dictionary.FindValue(actualKey)!; + + Debug.Assert(!Unsafe.IsNullRef(ref value), "the lookup result cannot be a null ref here"); + + return ref value; + } + + exists = false; + + return ref entry.value!; + } + } + + /// + /// A helper class containing APIs exposed through or . /// These methods are relatively niche and only used in specific scenarios, so adding them in a separate type avoids /// the additional overhead on each instantiation, especially in AOT scenarios. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs index 3b86548d20a7e..658716801fefa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace System.Collections.Generic @@ -362,6 +363,273 @@ public bool Remove(T item) #endregion + #region AlternateLookup + + /// + /// Provides a type that may be used to perform operations on a + /// using a instead of a . + /// + /// The alternate type of instance for performing lookups. + public struct AlternateLookup where TAlternate : allows ref struct + { + /// Initialize the instance. The set must have already been verified to have a compatible comparer. + internal AlternateLookup(HashSet set) + { + Debug.Assert(set is not null); + Debug.Assert(IsCompatibleItem(set)); + Set = set; + } + + /// Gets the against which this instance performs operations. + public HashSet Set { get; } + + /// Checks whether the set has a comparer compatible with . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsCompatibleItem(HashSet set) + { + Debug.Assert(set is not null); + return set._comparer is IAlternateEqualityComparer; + } + + /// Gets the set's alternate comparer. The set must have already been verified as compatible. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static IAlternateEqualityComparer GetAlternateComparer(HashSet set) + { + Debug.Assert(IsCompatibleItem(set)); + return Unsafe.As>(set._comparer); + } + + /// Adds the specified element to a set. + /// The element to add to the set. + /// true if the element is added to the set; false if the element is already present. + public bool Add(TAlternate item) + { + HashSet set = Set; + IAlternateEqualityComparer comparer = GetAlternateComparer(set); + + if (set._buckets == null) + { + set.Initialize(0); + } + Debug.Assert(set._buckets != null); + + Entry[]? entries = set._entries; + Debug.Assert(entries != null, "expected entries to be non-null"); + + int hashCode; + + uint collisionCount = 0; + ref int bucket = ref Unsafe.NullRef(); + + Debug.Assert(comparer is not null); + hashCode = comparer.GetHashCode(item); + bucket = ref set.GetBucketRef(hashCode); + int i = bucket - 1; // Value in _buckets is 1-based + while (i >= 0) + { + ref Entry entry = ref entries[i]; + if (entry.HashCode == hashCode && comparer.Equals(item, entry.Value)) + { + return false; + } + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop, which means a concurrent update has happened. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + + // Invoke comparer.Map before allocating space in the collection in order to avoid corrupting + // the collection if the operation fails. + T mappedItem = comparer.Create(item); + + int index; + if (set._freeCount > 0) + { + index = set._freeList; + set._freeCount--; + Debug.Assert((StartOfFreeList - entries![set._freeList].Next) >= -1, "shouldn't overflow because `next` cannot underflow"); + set._freeList = StartOfFreeList - entries[set._freeList].Next; + } + else + { + int count = set._count; + if (count == entries.Length) + { + set.Resize(); + bucket = ref set.GetBucketRef(hashCode); + } + index = count; + set._count = count + 1; + entries = set._entries; + } + + { + ref Entry entry = ref entries![index]; + entry.HashCode = hashCode; + entry.Next = bucket - 1; // Value in _buckets is 1-based + entry.Value = mappedItem; + bucket = index + 1; + set._version++; + } + + // Value types never rehash + if (!typeof(T).IsValueType && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) + { + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + set.Resize(entries.Length, forceNewHashCodes: true); + } + + return true; + } + + /// Removes the specified element from a set. + /// The element to remove. + /// true if the element is successfully found and removed; otherwise, false. + public bool Remove(TAlternate item) + { + HashSet set = Set; + IAlternateEqualityComparer comparer = GetAlternateComparer(set); + + if (set._buckets != null) + { + Entry[]? entries = set._entries; + Debug.Assert(entries != null, "entries should be non-null"); + + uint collisionCount = 0; + int last = -1; + + int hashCode = item is not null ? comparer!.GetHashCode(item) : 0; + + ref int bucket = ref set.GetBucketRef(hashCode); + int i = bucket - 1; // Value in buckets is 1-based + + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.HashCode == hashCode && comparer.Equals(item, entry.Value)) + { + if (last < 0) + { + bucket = entry.Next + 1; // Value in buckets is 1-based + } + else + { + entries[last].Next = entry.Next; + } + + Debug.Assert((StartOfFreeList - set._freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646"); + entry.Next = StartOfFreeList - set._freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.Value = default!; + } + + set._freeList = i; + set._freeCount++; + return true; + } + + last = i; + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + } + } + + return false; + } + + /// Determines whether a set contains the specified element. + /// The element to locate in the set. + /// true if the set contains the specified element; otherwise, false. + public bool Contains(TAlternate item) => !Unsafe.IsNullRef(in FindValue(item)); + + /// Searches the set for a given value and returns the equal value it finds, if any. + /// The value to search for. + /// The value from the set that the search found, or the default value of when the search yielded no match. + /// A value indicating whether the search was successful. + public bool TryGetValue(TAlternate equalValue, [MaybeNullWhen(false)] out T actualValue) + { + ref readonly T value = ref FindValue(equalValue); + if (!Unsafe.IsNullRef(in value)) + { + actualValue = value; + return true; + } + + actualValue = default!; + return false; + } + + /// Finds the item in the set and returns a reference to the found item, or a null reference if not found. + internal ref readonly T FindValue(TAlternate item) + { + HashSet set = Set; + IAlternateEqualityComparer comparer = GetAlternateComparer(set); + + ref Entry entry = ref Unsafe.NullRef(); + if (set._buckets != null) + { + Debug.Assert(set._entries != null, "expected entries to be != null"); + + int hashCode = comparer.GetHashCode(item); + int i = set.GetBucketRef(hashCode); + Entry[]? entries = set._entries; + uint collisionCount = 0; + i--; // Value in _buckets is 1-based; subtract 1 from i. We do it here so it fuses with the following conditional. + do + { + // Should be a while loop https://github.com/dotnet/runtime/issues/9422 + // Test in if to drop range check for following array access + if ((uint)i >= (uint)entries.Length) + { + goto ReturnNotFound; + } + + entry = ref entries[i]; + if (entry.HashCode == hashCode && comparer.Equals(item, entry.Value)) + { + goto ReturnFound; + } + + i = entry.Next; + + collisionCount++; + } while (collisionCount <= (uint)entries.Length); + + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + goto ConcurrentOperation; + } + + goto ReturnNotFound; + + ConcurrentOperation: + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + ReturnFound: + ref readonly T value = ref entry.Value; + Return: + return ref value; + ReturnNotFound: + value = ref Unsafe.NullRef(); + goto Return; + } + } + #endregion + #region IEnumerable methods public Enumerator GetEnumerator() => new Enumerator(this); diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAlternateEqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAlternateEqualityComparer.cs new file mode 100644 index 0000000000000..9a9c405278f89 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAlternateEqualityComparer.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Collections.Generic +{ + /// + /// Implemented by an to support comparing + /// a instance with a instance. + /// + /// The alternate type to compare. + /// The type to compare. + public interface IAlternateEqualityComparer where TAlternate : allows ref struct + { + /// Determines whether the specified equals the specified . + /// The instance of type to compare. + /// The instance of type to compare. + /// if the specified instances are equal; otherwise, . + bool Equals(TAlternate alternate, T other); + + /// Returns a hash code for the specified alternate instance. + /// The instance of type for which to get a hash code. + /// A hash code for the specified instance. + /// + /// This interface is intended to be implemented on a type that also implements . + /// The result of this method should return the same hash code as would invoking the + /// method on any for which + /// returns . + /// + int GetHashCode(TAlternate alternate); + + /// + /// Creates a that is considered by to be equal + /// to the specified . + /// + /// The instance of type for which an equal is required. + /// A considered equal to the specified . + T Create(TAlternate alternate); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerable.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerable.cs index 097c31d12679d..bf7dcf51416c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerable.cs @@ -8,6 +8,9 @@ namespace System.Collections.Generic /// Exposes an enumerator that provides asynchronous iteration over values of a specified type. /// The type of values to enumerate. public interface IAsyncEnumerable +#if NET9_0_OR_GREATER + where T : allows ref struct +#endif { /// Returns an enumerator that iterates asynchronously through the collection. /// A that may be used to cancel the asynchronous iteration. diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerator.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerator.cs index fc1a9d5e1d93b..f5249067de14c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IAsyncEnumerator.cs @@ -8,6 +8,9 @@ namespace System.Collections.Generic /// Supports a simple asynchronous iteration over a generic collection. /// The type of objects to enumerate. public interface IAsyncEnumerator : IAsyncDisposable +#if NET9_0_OR_GREATER + where T : allows ref struct +#endif { /// Advances the enumerator asynchronously to the next element of the collection. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerable.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerable.cs index 096f0d9dfff40..1cdfaeb34a0f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerable.cs @@ -9,6 +9,7 @@ namespace System.Collections.Generic { // Implement this interface if you need to support foreach semantics. public interface IEnumerable : IEnumerable + where T : allows ref struct { // Returns an IEnumerator for this enumerable Object. The enumerator provides // a simple way to access all the contents of a collection. diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerator.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerator.cs index 4569347164227..8d32647af98a5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IEnumerator.cs @@ -6,6 +6,7 @@ namespace System.Collections.Generic // Base interface for all generic enumerators, providing a simple approach // to iterating over a collection. public interface IEnumerator : IDisposable, IEnumerator + where T : allows ref struct { // Returns the current element of the enumeration. The returned value is // undefined before the first call to MoveNext and following a @@ -16,5 +17,10 @@ public interface IEnumerator : IDisposable, IEnumerator { get; } + + // NOTE: An implementation of an enumerator using a ref struct T will + // not be able to implement IEnumerator.Current to return that T (as + // doing so would require boxing). It should throw a NotSupportedException + // from that property implementation. } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs index de01ba5bdc5ad..b3a5d8d801313 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace System.Collections.Generic @@ -19,7 +20,7 @@ public class NonRandomizedStringEqualityComparer : IEqualityComparer, I // that was passed in to the ctor. The caller chooses one of these singletons so that the // GetUnderlyingEqualityComparer method can return the correct value. - private static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new OrdinalComparer(EqualityComparer.Default); + private static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new DefaultComparer(EqualityComparer.Default); private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinal = new OrdinalComparer(StringComparer.Ordinal); private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(StringComparer.OrdinalIgnoreCase); @@ -73,10 +74,15 @@ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext contex info.SetType(typeof(GenericEqualityComparer)); } - private sealed class OrdinalComparer : NonRandomizedStringEqualityComparer + // TODO https://github.com/dotnet/runtime/issues/102906: + // This custom class exists because EqualityComparer.Default doesn't implement IAlternateEqualityComparer, string>. + // If OrdinalComparer were used, then a dictionary created with a null/Default comparer would be using a comparer that does + // implement IAlternateEqualityComparer, string>, but only until it hits a collision and switches to the randomized comparer. + // Once EqualityComparer.Default implements IAlternateEqualityComparer, string>, we can remove this class, and change + // WrappedAroundDefaultComparer to be an instance of OrdinalComparer. + private sealed class DefaultComparer : NonRandomizedStringEqualityComparer { - internal OrdinalComparer(IEqualityComparer wrappedComparer) - : base(wrappedComparer) + internal DefaultComparer(IEqualityComparer wrappedComparer) : base(wrappedComparer) { } @@ -87,13 +93,43 @@ public override int GetHashCode(string? obj) Debug.Assert(obj != null, "This implementation is only called from first-party collection types that guarantee non-null parameters."); return obj.GetNonRandomizedHashCode(); } + } + + private sealed class OrdinalComparer : NonRandomizedStringEqualityComparer, IAlternateEqualityComparer, string?> + { + internal OrdinalComparer(IEqualityComparer wrappedComparer) : base(wrappedComparer) + { + } + + public override bool Equals(string? x, string? y) => string.Equals(x, y); + + public override int GetHashCode(string? obj) + { + Debug.Assert(obj != null, "This implementation is only called from first-party collection types that guarantee non-null parameters."); + return obj.GetNonRandomizedHashCode(); + } + + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + string.GetNonRandomizedHashCode(span); + + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return span.SequenceEqual(target); + } + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); } - private sealed class OrdinalIgnoreCaseComparer : NonRandomizedStringEqualityComparer + private sealed class OrdinalIgnoreCaseComparer : NonRandomizedStringEqualityComparer, IAlternateEqualityComparer, string?> { - internal OrdinalIgnoreCaseComparer(IEqualityComparer wrappedComparer) - : base(wrappedComparer) + internal OrdinalIgnoreCaseComparer(IEqualityComparer wrappedComparer) : base(wrappedComparer) { } @@ -105,6 +141,23 @@ public override int GetHashCode(string? obj) return obj.GetNonRandomizedHashCodeOrdinalIgnoreCase(); } + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + string.GetNonRandomizedHashCodeOrdinalIgnoreCase(span); + + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return span.EqualsOrdinalIgnoreCase(target); + } + + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); + internal override RandomizedStringEqualityComparer GetRandomizedEqualityComparer() { return RandomizedStringEqualityComparer.Create(_underlyingComparer, ignoreCase: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs index 77f1153181f67..5baf1e5076b6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs @@ -571,26 +571,16 @@ public static int Compare(DateTimeOffset first, DateTimeOffset second) => int IComparable.CompareTo(object? obj) { if (obj == null) return 1; - if (!(obj is DateTimeOffset)) + if (obj is not DateTimeOffset other) { throw new ArgumentException(SR.Arg_MustBeDateTimeOffset); } - DateTime objUtc = ((DateTimeOffset)obj).UtcDateTime; - DateTime utc = UtcDateTime; - if (utc > objUtc) return 1; - if (utc < objUtc) return -1; - return 0; + return DateTime.Compare(UtcDateTime, other.UtcDateTime); } - public int CompareTo(DateTimeOffset other) - { - DateTime otherUtc = other.UtcDateTime; - DateTime utc = UtcDateTime; - if (utc > otherUtc) return 1; - if (utc < otherUtc) return -1; - return 0; - } + public int CompareTo(DateTimeOffset other) => + DateTime.Compare(UtcDateTime, other.UtcDateTime); // Checks if this DateTimeOffset is equal to a given object. Returns // true if the given object is a boxed DateTimeOffset and its value diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 48cab155a9425..a505c8de02a53 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -338,7 +338,7 @@ public static string GetName(Type eventSourceType) /// /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is - /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema. + /// documented at in EventManifest Schema https://learn.microsoft.com/windows/desktop/WES/eventmanifestschema-schema. /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff. /// @@ -359,7 +359,7 @@ public static string GetName(Type eventSourceType) } /// /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is - /// documented at in EventManifest Schema https://docs.microsoft.com/en-us/windows/desktop/WES/eventmanifestschema-schema. + /// documented at in EventManifest Schema https://learn.microsoft.com/windows/desktop/WES/eventmanifestschema-schema. /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This /// ensures that the entries in the event log will be "optimally" localized. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 26e65460ef24f..8d66af450f327 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -354,33 +354,33 @@ public override int GetHashCode() public override string ToString() { - return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(m_value, null, NumberFormatInfo.CurrentInfo); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { - return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(m_value, format, NumberFormatInfo.CurrentInfo); } public string ToString(IFormatProvider? provider) { - return Number.FormatDouble(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(m_value, null, NumberFormatInfo.GetInstance(provider)); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { - return Number.FormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider)); } public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } /// public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); } public static double Parse(string s) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null); @@ -2335,6 +2335,10 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static ulong IBinaryFloatParseAndFormatInfo.FloatToBits(double value) => BitConverter.DoubleToUInt64Bits(value); + static int IBinaryFloatParseAndFormatInfo.MaxRoundTripDigits => 17; + + static int IBinaryFloatParseAndFormatInfo.MaxPrecisionCustomFormat => 15; + // // Helpers // diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs index 419f1a0170a2f..2f349d8cb5961 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs @@ -11,7 +11,7 @@ namespace System { public abstract partial class Enum { - internal sealed class EnumInfo + internal sealed partial class EnumInfo where TStorage : struct, INumber { public readonly bool HasFlagsAttribute; diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs index 7e78ede36eb63..82f7271c780fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -245,7 +245,7 @@ private static unsafe string[] SegmentCommandLine(char* cmdLine) // character is simply accepted. Fancier handling is not required // because the program name must be a legal NTFS/HPFS file name. // Note that the double-quote characters are not copied, nor do they - // contribyte to character_count. + // contribute to character_count. bool inQuotes = false; stringBuilder = new ValueStringBuilder(stringBuffer); diff --git a/src/libraries/System.Private.CoreLib/src/System/Function.cs b/src/libraries/System.Private.CoreLib/src/System/Function.cs index 519d1cd82e4b0..a8554896437df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Function.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Function.cs @@ -3,21 +3,190 @@ namespace System { - public delegate TResult Func(); - public delegate TResult Func(T arg); - public delegate TResult Func(T1 arg1, T2 arg2); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); + public delegate TResult Func() + where TResult : allows ref struct; + + public delegate TResult Func(T arg) + where T : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2) + where T1 : allows ref struct + where T2 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where T16 : allows ref struct + where TResult : allows ref struct; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs index 152b55a3e2f70..bb24e9242fa0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs @@ -150,7 +150,7 @@ private unsafe int NlsGetHashCodeOfString(ReadOnlySpan source, CompareOpti // Note in calls to LCMapStringEx below, the input buffer is specified in wchars (and wchar count), // but the output buffer is specified in bytes (and byte count). This is because when generating // sort keys, LCMapStringEx treats the output buffer as containing opaque binary data. - // See https://docs.microsoft.com/en-us/windows/desktop/api/winnls/nf-winnls-lcmapstringex. + // See https://learn.microsoft.com/windows/desktop/api/winnls/nf-winnls-lcmapstringex. byte[]? borrowedArr = null; Span span = sortKeyLength <= 512 ? 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 e8935a6d2439c..8ea2223764d8d 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 @@ -117,7 +117,7 @@ private unsafe int JsIndexOfCore(ReadOnlySpan source, ReadOnlySpan t } } - // there are chars that are ignored by ICU hashing algorithm but not ignored by invariant hashing + // there are chars that are considered equal by HybridGlobalization but do not have equal hashes when binary hashed // Control: 1105 (out of 1105) // Format: 697 (out of 731) // OtherPunctuation: 6919 (out of 7004) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 911fb9f7184a3..7ce5edfc3fbb8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -958,68 +958,11 @@ public int CompareTo(object? value) { return 1; } - if (!(value is Guid)) + if (value is not Guid other) { throw new ArgumentException(SR.Arg_MustBeGuid, nameof(value)); } - Guid g = (Guid)value; - - if (g._a != _a) - { - return GetResult((uint)_a, (uint)g._a); - } - - if (g._b != _b) - { - return GetResult((uint)_b, (uint)g._b); - } - - if (g._c != _c) - { - return GetResult((uint)_c, (uint)g._c); - } - - if (g._d != _d) - { - return GetResult(_d, g._d); - } - - if (g._e != _e) - { - return GetResult(_e, g._e); - } - - if (g._f != _f) - { - return GetResult(_f, g._f); - } - - if (g._g != _g) - { - return GetResult(_g, g._g); - } - - if (g._h != _h) - { - return GetResult(_h, g._h); - } - - if (g._i != _i) - { - return GetResult(_i, g._i); - } - - if (g._j != _j) - { - return GetResult(_j, g._j); - } - - if (g._k != _k) - { - return GetResult(_k, g._k); - } - - return 0; + return CompareTo(other); } public int CompareTo(Guid value) diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index 533521970c7e2..73cf14403d59e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -505,7 +505,7 @@ public override int GetHashCode() /// public override string ToString() { - return Number.FormatHalf(this, null, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(this, null, NumberFormatInfo.CurrentInfo); } /// @@ -513,7 +513,7 @@ public override string ToString() /// public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { - return Number.FormatHalf(this, format, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(this, format, NumberFormatInfo.CurrentInfo); } /// @@ -521,7 +521,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin /// public string ToString(IFormatProvider? provider) { - return Number.FormatHalf(this, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(this, null, NumberFormatInfo.GetInstance(provider)); } /// @@ -529,7 +529,7 @@ public string ToString(IFormatProvider? provider) /// public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { - return Number.FormatHalf(this, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(this, format, NumberFormatInfo.GetInstance(provider)); } /// @@ -542,13 +542,13 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin /// public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } /// public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); } // @@ -2373,5 +2373,9 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static Half IBinaryFloatParseAndFormatInfo.BitsToFloat(ulong bits) => BitConverter.UInt16BitsToHalf((ushort)(bits)); static ulong IBinaryFloatParseAndFormatInfo.FloatToBits(Half value) => BitConverter.HalfToUInt16Bits(value); + + static int IBinaryFloatParseAndFormatInfo.MaxRoundTripDigits => 5; + + static int IBinaryFloatParseAndFormatInfo.MaxPrecisionCustomFormat => 5; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs index 81f2e063a847e..9b2977fa51b86 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs @@ -49,7 +49,7 @@ public static string GetFullPath(string path) if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); - // Embedded null characters are the only invalid character case we trully care about. + // Embedded null characters are the only invalid character case we truly care about. // This is because the nulls will signal the end of the string to Win32 and therefore have // unpredictable results. if (path.Contains('\0')) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs index e5c8e17da23e9..151a0abfc84fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs @@ -57,7 +57,7 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); return errorCode switch { - // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position: + // https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position: // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" Interop.Errors.ERROR_HANDLE_EOF => numBytesRead, @@ -447,7 +447,7 @@ internal static void WriteGatherAtOffset(SafeFileHandle handle, IReadOnlyList handle.IsAsync && ((handle.GetFileOptions() & SafeFileHandle.NoBuffering) != 0); @@ -738,7 +738,7 @@ private static async ValueTask WriteGatherAtOffsetMultipleSyscallsAsync(SafeFile result->OffsetHigh = (int)(fileOffset >> 32); } - // From https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult: + // From https://learn.microsoft.com/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult: // "If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed. // Use of file, named pipe, or communications-device handles for this purpose is discouraged. // It is safer to use an event object because of the confusion that can occur when multiple simultaneous overlapped operations @@ -788,7 +788,7 @@ internal static bool IsEndOfFile(int errorCode, SafeFileHandle handle, long file } } - // From https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering: + // From https://learn.microsoft.com/windows/win32/fileio/file-buffering: // "File access sizes, including the optional file offset in the OVERLAPPED structure, // if specified, must be for a number of bytes that is an integer multiple of the volume sector size." // So if buffer and physical sector size is 4096 and the file size is 4097: diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DiyFp.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DiyFp.cs index 268bd53695e7d..49148306fff0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DiyFp.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DiyFp.cs @@ -17,10 +17,6 @@ internal static partial class Number // DiyFp are not designed to contain special doubles (NaN and Infinity). internal readonly ref struct DiyFp { - public const int DoubleImplicitBitIndex = 52; - public const int SingleImplicitBitIndex = 23; - public const int HalfImplicitBitIndex = 10; - public const int SignificandSize = 64; public readonly ulong f; @@ -33,60 +29,21 @@ internal readonly ref struct DiyFp // // Precondition: // The value encoded by value must be greater than 0. - public static DiyFp CreateAndGetBoundaries(double value, out DiyFp mMinus, out DiyFp mPlus) + public static DiyFp CreateAndGetBoundaries(TNumber value, out DiyFp mMinus, out DiyFp mPlus) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo { - var result = new DiyFp(value); - result.GetBoundaries(DoubleImplicitBitIndex, out mMinus, out mPlus); + var result = Create(value); + result.GetBoundaries(TNumber.DenormalMantissaBits, out mMinus, out mPlus); return result; } - // Computes the two boundaries of value. - // - // The bigger boundary (mPlus) is normalized. - // The lower boundary has the same exponent as mPlus. - // - // Precondition: - // The value encoded by value must be greater than 0. - public static DiyFp CreateAndGetBoundaries(float value, out DiyFp mMinus, out DiyFp mPlus) - { - var result = new DiyFp(value); - result.GetBoundaries(SingleImplicitBitIndex, out mMinus, out mPlus); - return result; - } - - // Computes the two boundaries of value. - // - // The bigger boundary (mPlus) is normalized. - // The lower boundary has the same exponent as mPlus. - // - // Precondition: - // The value encoded by value must be greater than 0. - public static DiyFp CreateAndGetBoundaries(Half value, out DiyFp mMinus, out DiyFp mPlus) - { - var result = new DiyFp(value); - result.GetBoundaries(HalfImplicitBitIndex, out mMinus, out mPlus); - return result; - } - - public DiyFp(double value) - { - Debug.Assert(double.IsFinite(value)); - Debug.Assert(value > 0.0); - f = ExtractFractionAndBiasedExponent(value, out e); - } - - public DiyFp(float value) - { - Debug.Assert(float.IsFinite(value)); - Debug.Assert(value > 0.0f); - f = ExtractFractionAndBiasedExponent(value, out e); - } - - public DiyFp(Half value) + public static DiyFp Create(TNumber value) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo { - Debug.Assert(Half.IsFinite(value)); - Debug.Assert((float)value > 0.0f); - f = ExtractFractionAndBiasedExponent(value, out e); + Debug.Assert(TNumber.IsFinite(value)); + Debug.Assert(value > TNumber.Zero); + ulong f = ExtractFractionAndBiasedExponent(value, out int e); + return new DiyFp(f, e); } public DiyFp(ulong f, int e) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs index 10fbfcdbee547..038fdb23947dd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs @@ -10,82 +10,23 @@ namespace System // The backing algorithm and the proofs behind it are described in more detail here: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf internal static partial class Number { - public static void Dragon4Double(double value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) + public static unsafe void Dragon4(TNumber value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo { - double v = double.IsNegative(value) ? -value : value; + TNumber v = TNumber.IsNegative(value) ? -value : value; - Debug.Assert(v > 0); - Debug.Assert(double.IsFinite(v)); + Debug.Assert(v > TNumber.Zero); + Debug.Assert(TNumber.IsFinite(v)); ulong mantissa = ExtractFractionAndBiasedExponent(value, out int exponent); uint mantissaHighBitIdx; bool hasUnequalMargins = false; - if ((mantissa >> DiyFp.DoubleImplicitBitIndex) != 0) + if ((mantissa >> TNumber.DenormalMantissaBits) != 0) { - mantissaHighBitIdx = DiyFp.DoubleImplicitBitIndex; - hasUnequalMargins = (mantissa == (1UL << DiyFp.DoubleImplicitBitIndex)); - } - else - { - Debug.Assert(mantissa != 0); - mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa); - } - - int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent)); - - number.Scale = decimalExponent + 1; - number.Digits[length] = (byte)('\0'); - number.DigitsCount = length; - } - - public static unsafe void Dragon4Half(Half value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) - { - Half v = Half.IsNegative(value) ? Half.Negate(value) : value; - - Debug.Assert((double)v > 0.0); - Debug.Assert(Half.IsFinite(v)); - - ushort mantissa = ExtractFractionAndBiasedExponent(value, out int exponent); - - uint mantissaHighBitIdx; - bool hasUnequalMargins = false; - - if ((mantissa >> DiyFp.HalfImplicitBitIndex) != 0) - { - mantissaHighBitIdx = DiyFp.HalfImplicitBitIndex; - hasUnequalMargins = (mantissa == (1U << DiyFp.HalfImplicitBitIndex)); - } - else - { - Debug.Assert(mantissa != 0); - mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa); - } - - int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent)); - - number.Scale = decimalExponent + 1; - number.Digits[length] = (byte)('\0'); - number.DigitsCount = length; - } - - public static unsafe void Dragon4Single(float value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number) - { - float v = float.IsNegative(value) ? -value : value; - - Debug.Assert(v > 0); - Debug.Assert(float.IsFinite(v)); - - uint mantissa = ExtractFractionAndBiasedExponent(value, out int exponent); - - uint mantissaHighBitIdx; - bool hasUnequalMargins = false; - - if ((mantissa >> DiyFp.SingleImplicitBitIndex) != 0) - { - mantissaHighBitIdx = DiyFp.SingleImplicitBitIndex; - hasUnequalMargins = (mantissa == (1U << DiyFp.SingleImplicitBitIndex)); + mantissaHighBitIdx = TNumber.DenormalMantissaBits; + hasUnequalMargins = (mantissa == (1U << TNumber.DenormalMantissaBits)); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 2a89bf9638529..928eaea0d9540 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -252,23 +252,6 @@ internal static partial class Number { internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value - // SinglePrecision and DoublePrecision represent the maximum number of digits required - // to guarantee that any given Single or Double can roundtrip. Some numbers may require - // less, but none will require more. - private const int HalfPrecision = 5; - private const int SinglePrecision = 9; - private const int DoublePrecision = 17; - - // SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that - // custom format strings return the same string as in previous releases when the format - // would return x digits or less (where x is the value of the corresponding constant). - // In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse - // the format and determine exactly how many digits are being requested and whether they - // represent "significant digits" or "digits after the decimal point". - private const int HalfPrecisionCustomFormat = 5; - private const int SinglePrecisionCustomFormat = 7; - private const int DoublePrecisionCustomFormat = 15; - /// The non-inclusive upper bound of . /// /// This is a semi-arbitrary bound. For mono, which is often used for more size-constrained workloads, @@ -394,28 +377,6 @@ internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuff number.CheckConsistency(); } - public static string FormatDouble(double value, string? format, NumberFormatInfo info) - { - var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); - string result = FormatDouble(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); - vlb.Dispose(); - return result; - } - - public static bool TryFormatDouble(double value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar - { - var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); - string? s = FormatDouble(ref vlb, value, format, info); - - Debug.Assert(s is null || typeof(TChar) == typeof(char)); - bool success = s != null ? - TryCopyTo(s, destination, out charsWritten) : - vlb.TryCopyTo(destination, out charsWritten); - - vlb.Dispose(); - return success; - } - private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int precision, NumberFormatInfo info, out bool isSignificantDigits) { if (fmt == 0) @@ -537,105 +498,23 @@ private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int preci return maxDigits; } - /// Formats the specified value according to the specified format and info. - /// - /// Non-null if an existing string can be returned, in which case the builder will be unmodified. - /// Null if no existing string was returned, in which case the formatted output is in the builder. - /// - private static unsafe string? FormatDouble(ref ValueListBuilder vlb, double value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar - { - if (!double.IsFinite(value)) - { - if (double.IsNaN(value)) - { - if (typeof(TChar) == typeof(char)) - { - return info.NaNSymbol; - } - else - { - vlb.Append(info.NaNSymbolTChar()); - return null; - } - } - - if (typeof(TChar) == typeof(char)) - { - return double.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - } - else - { - vlb.Append(double.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); - return null; - } - } - - char fmt = ParseFormatSpecifier(format, out int precision); - byte* pDigits = stackalloc byte[DoubleNumberBufferLength]; - - if (fmt == '\0') - { - // For back-compat we currently specially treat the precision for custom - // format specifiers. The constant has more details as to why. - precision = DoublePrecisionCustomFormat; - } - - NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength); - number.IsNegative = double.IsNegative(value); - - // We need to track the original precision requested since some formats - // accept values like 0 and others may require additional fixups. - int nMaxDigits = GetFloatingPointMaxDigitsAndPrecision(fmt, ref precision, info, out bool isSignificantDigits); - - if ((value != 0.0) && (!isSignificantDigits || !Grisu3.TryRunDouble(value, precision, ref number))) - { - Dragon4Double(value, precision, isSignificantDigits, ref number); - } - - number.CheckConsistency(); - - // When the number is known to be roundtrippable (either because we requested it be, or - // because we know we have enough digits to satisfy roundtrippability), we should validate - // that the number actually roundtrips back to the original result. - - Debug.Assert(((precision != -1) && (precision < DoublePrecision)) || (BitConverter.DoubleToInt64Bits(value) == BitConverter.DoubleToInt64Bits(NumberToFloat(ref number)))); - - if (fmt != 0) - { - if (precision == -1) - { - Debug.Assert((fmt == 'G') || (fmt == 'g') || (fmt == 'R') || (fmt == 'r')); - - // For the roundtrip and general format specifiers, when returning the shortest roundtrippable - // string, we need to update the maximum number of digits to be the greater of number.DigitsCount - // or DoublePrecision. This ensures that we continue returning "pretty" strings for values with - // less digits. One example this fixes is "-60", which would otherwise be formatted as "-6E+01" - // since DigitsCount would be 1 and the formatter would almost immediately switch to scientific notation. - - nMaxDigits = Math.Max(number.DigitsCount, DoublePrecision); - } - NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); - } - else - { - Debug.Assert(precision == DoublePrecisionCustomFormat); - NumberToStringFormat(ref vlb, ref number, format, info); - } - return null; - } - - public static string FormatSingle(float value, string? format, NumberFormatInfo info) + public static string FormatFloat(TNumber value, string? format, NumberFormatInfo info) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo { var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); - string result = FormatSingle(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); + string result = FormatFloat(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); vlb.Dispose(); return result; } - public static bool TryFormatSingle(float value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + public static bool TryFormatFloat(TNumber value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo + where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); - string? s = FormatSingle(ref vlb, value, format, info); + string? s = FormatFloat(ref vlb, value, format, info); Debug.Assert(s is null || typeof(TChar) == typeof(char)); bool success = s != null ? @@ -651,110 +530,15 @@ public static bool TryFormatSingle(float value, ReadOnlySpan format /// Non-null if an existing string can be returned, in which case the builder will be unmodified. /// Null if no existing string was returned, in which case the formatted output is in the builder. /// - private static unsafe string? FormatSingle(ref ValueListBuilder vlb, float value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar - { - Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - - if (!float.IsFinite(value)) - { - if (float.IsNaN(value)) - { - if (typeof(TChar) == typeof(char)) - { - return info.NaNSymbol; - } - else - { - vlb.Append(info.NaNSymbolTChar()); - return null; - } - } - - if (typeof(TChar) == typeof(char)) - { - return float.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - } - else - { - vlb.Append(float.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); - return null; - } - } - - char fmt = ParseFormatSpecifier(format, out int precision); - byte* pDigits = stackalloc byte[SingleNumberBufferLength]; - - if (fmt == '\0') - { - // For back-compat we currently specially treat the precision for custom - // format specifiers. The constant has more details as to why. - precision = SinglePrecisionCustomFormat; - } - - NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength); - number.IsNegative = float.IsNegative(value); - - // We need to track the original precision requested since some formats - // accept values like 0 and others may require additional fixups. - int nMaxDigits = GetFloatingPointMaxDigitsAndPrecision(fmt, ref precision, info, out bool isSignificantDigits); - - if ((value != default) && (!isSignificantDigits || !Grisu3.TryRunSingle(value, precision, ref number))) - { - Dragon4Single(value, precision, isSignificantDigits, ref number); - } - - number.CheckConsistency(); - - // When the number is known to be roundtrippable (either because we requested it be, or - // because we know we have enough digits to satisfy roundtrippability), we should validate - // that the number actually roundtrips back to the original result. - - Debug.Assert(((precision != -1) && (precision < SinglePrecision)) || (BitConverter.SingleToInt32Bits(value) == BitConverter.SingleToInt32Bits(NumberToFloat(ref number)))); - - if (fmt != 0) - { - if (precision == -1) - { - Debug.Assert((fmt == 'G') || (fmt == 'g') || (fmt == 'R') || (fmt == 'r')); - - // For the roundtrip and general format specifiers, when returning the shortest roundtrippable - // string, we need to update the maximum number of digits to be the greater of number.DigitsCount - // or SinglePrecision. This ensures that we continue returning "pretty" strings for values with - // less digits. One example this fixes is "-60", which would otherwise be formatted as "-6E+01" - // since DigitsCount would be 1 and the formatter would almost immediately switch to scientific notation. - - nMaxDigits = Math.Max(number.DigitsCount, SinglePrecision); - } - NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); - } - else - { - Debug.Assert(precision == SinglePrecisionCustomFormat); - NumberToStringFormat(ref vlb, ref number, format, info); - } - return null; - } - - public static string FormatHalf(Half value, string? format, NumberFormatInfo info) - { - var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); - string result = FormatHalf(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); - vlb.Dispose(); - return result; - } - - /// Formats the specified value according to the specified format and info. - /// - /// Non-null if an existing string can be returned, in which case the builder will be unmodified. - /// Null if no existing string was returned, in which case the formatted output is in the builder. - /// - private static unsafe string? FormatHalf(ref ValueListBuilder vlb, Half value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar + private static unsafe string? FormatFloat(ref ValueListBuilder vlb, TNumber value, ReadOnlySpan format, NumberFormatInfo info) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo + where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - if (!Half.IsFinite(value)) + if (!TNumber.IsFinite(value)) { - if (Half.IsNaN(value)) + if (TNumber.IsNaN(value)) { if (typeof(TChar) == typeof(char)) { @@ -769,33 +553,33 @@ public static string FormatHalf(Half value, string? format, NumberFormatInfo inf if (typeof(TChar) == typeof(char)) { - return Half.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + return TNumber.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; } else { - vlb.Append(Half.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); + vlb.Append(TNumber.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); return null; } } char fmt = ParseFormatSpecifier(format, out int precision); - byte* pDigits = stackalloc byte[HalfNumberBufferLength]; + byte* pDigits = stackalloc byte[TNumber.NumberBufferLength]; if (fmt == '\0') { - precision = HalfPrecisionCustomFormat; + precision = TNumber.MaxPrecisionCustomFormat; } - NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, HalfNumberBufferLength); - number.IsNegative = Half.IsNegative(value); + NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, TNumber.NumberBufferLength); + number.IsNegative = TNumber.IsNegative(value); // We need to track the original precision requested since some formats // accept values like 0 and others may require additional fixups. int nMaxDigits = GetFloatingPointMaxDigitsAndPrecision(fmt, ref precision, info, out bool isSignificantDigits); - if ((value != default) && (!isSignificantDigits || !Grisu3.TryRunHalf(value, precision, ref number))) + if ((value != default) && (!isSignificantDigits || !Grisu3.TryRun(value, precision, ref number))) { - Dragon4Half(value, precision, isSignificantDigits, ref number); + Dragon4(value, precision, isSignificantDigits, ref number); } number.CheckConsistency(); @@ -804,7 +588,7 @@ public static string FormatHalf(Half value, string? format, NumberFormatInfo inf // because we know we have enough digits to satisfy roundtrippability), we should validate // that the number actually roundtrips back to the original result. - Debug.Assert(((precision != -1) && (precision < HalfPrecision)) || (BitConverter.HalfToInt16Bits(value) == BitConverter.HalfToInt16Bits(NumberToFloat(ref number)))); + Debug.Assert(((precision != -1) && (precision < TNumber.MaxRoundTripDigits)) || (TNumber.FloatToBits(value) == TNumber.FloatToBits(NumberToFloat(ref number)))); if (fmt != 0) { @@ -818,34 +602,18 @@ public static string FormatHalf(Half value, string? format, NumberFormatInfo inf // less digits. One example this fixes is "-60", which would otherwise be formatted as "-6E+01" // since DigitsCount would be 1 and the formatter would almost immediately switch to scientific notation. - nMaxDigits = Math.Max(number.DigitsCount, HalfPrecision); + nMaxDigits = Math.Max(number.DigitsCount, TNumber.MaxRoundTripDigits); } NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); } else { - Debug.Assert(precision == HalfPrecisionCustomFormat); + Debug.Assert(precision == TNumber.MaxPrecisionCustomFormat); NumberToStringFormat(ref vlb, ref number, format, info); } return null; } - public static bool TryFormatHalf(Half value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar - { - Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - - var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); - string? s = FormatHalf(ref vlb, value, format, info); - - Debug.Assert(s is null || typeof(TChar) == typeof(char)); - bool success = s != null ? - TryCopyTo(s, destination, out charsWritten) : - vlb.TryCopyTo(destination, out charsWritten); - - vlb.Dispose(); - return success; - } - private static bool TryCopyTo(string source, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -2867,5 +2635,38 @@ private static uint ExtractFractionAndBiasedExponent(float value, out int expone return fraction; } + + private static ulong ExtractFractionAndBiasedExponent(TNumber value, out int exponent) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo + { + ulong bits = TNumber.FloatToBits(value); + ulong fraction = (bits & TNumber.DenormalMantissaMask); + exponent = ((int)(bits >> TNumber.DenormalMantissaBits) & TNumber.InfinityExponent); + + if (exponent != 0) + { + // For normalized value, + // value = 1.fraction * 2^(exp - ExponentBias) + // = (1 + mantissa / 2^TrailingSignificandLength) * 2^(exp - ExponentBias) + // = (2^TrailingSignificandLength + mantissa) * 2^(exp - ExponentBias - TrailingSignificandLength) + // + // So f = (2^TrailingSignificandLength + mantissa), e = exp - ExponentBias - TrailingSignificandLength; + + fraction |= (1UL << TNumber.DenormalMantissaBits); + exponent -= TNumber.ExponentBias + TNumber.DenormalMantissaBits; + } + else + { + // For denormalized value, + // value = 0.fraction * 2^(MinBinaryExponent) + // = (mantissa / 2^TrailingSignificandLength) * 2^(MinBinaryExponent) + // = mantissa * 2^(MinBinaryExponent - TrailingSignificandLength) + // = mantissa * 2^(MinBinaryExponent - TrailingSignificandLength) + // So f = mantissa, e = MinBinaryExponent - TrailingSignificandLength + exponent = TNumber.MinBinaryExponent - TNumber.DenormalMantissaBits; + } + + return fraction; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs index 4a746b38cc2c3..e4a25b9b5939c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs @@ -321,12 +321,13 @@ internal static class Grisu3 1000000000, // 10^9 ]; - public static bool TryRunDouble(double value, int requestedDigits, ref NumberBuffer number) + public static bool TryRun(TNumber value, int requestedDigits, ref NumberBuffer number) + where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo { - double v = double.IsNegative(value) ? -value : value; + TNumber v = TNumber.IsNegative(value) ? -value : value; - Debug.Assert(v > 0); - Debug.Assert(double.IsFinite(v)); + Debug.Assert(v > TNumber.Zero); + Debug.Assert(TNumber.IsFinite(v)); int length; int decimalExponent; @@ -339,75 +340,7 @@ public static bool TryRunDouble(double value, int requestedDigits, ref NumberBuf } else { - DiyFp w = new DiyFp(v).Normalize(); - result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent); - } - - if (result) - { - Debug.Assert((requestedDigits == -1) || (length == requestedDigits)); - - number.Scale = length + decimalExponent; - number.Digits[length] = (byte)('\0'); - number.DigitsCount = length; - } - - return result; - } - - public static bool TryRunHalf(Half value, int requestedDigits, ref NumberBuffer number) - { - Half v = Half.IsNegative(value) ? Half.Negate(value) : value; - - Debug.Assert((double)v > 0); - Debug.Assert(Half.IsFinite(v)); - - int length; - int decimalExponent; - bool result; - - if (requestedDigits == -1) - { - DiyFp w = DiyFp.CreateAndGetBoundaries(v, out DiyFp boundaryMinus, out DiyFp boundaryPlus).Normalize(); - result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.Digits, out length, out decimalExponent); - } - else - { - DiyFp w = new DiyFp(v).Normalize(); - result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent); - } - - if (result) - { - Debug.Assert((requestedDigits == -1) || (length == requestedDigits)); - - number.Scale = length + decimalExponent; - number.Digits[length] = (byte)('\0'); - number.DigitsCount = length; - } - - return result; - } - - public static bool TryRunSingle(float value, int requestedDigits, ref NumberBuffer number) - { - float v = float.IsNegative(value) ? -value : value; - - Debug.Assert(v > 0); - Debug.Assert(float.IsFinite(v)); - - int length; - int decimalExponent; - bool result; - - if (requestedDigits == -1) - { - DiyFp w = DiyFp.CreateAndGetBoundaries(v, out DiyFp boundaryMinus, out DiyFp boundaryPlus).Normalize(); - result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.Digits, out length, out decimalExponent); - } - else - { - DiyFp w = new DiyFp(v).Normalize(); + DiyFp w = DiyFp.Create(v).Normalize(); result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 852b979492c2d..263f60ad0ba10 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -85,6 +85,18 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI static abstract TSelf BitsToFloat(ulong bits); static abstract ulong FloatToBits(TSelf value); + + // Maximum number of digits required to guarantee that any given floating point + // number can roundtrip. Some numbers may require less, but none will require more. + static abstract int MaxRoundTripDigits { get; } + + // SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that + // custom format strings return the same string as in previous releases when the format + // would return x digits or less (where x is the value of the corresponding constant). + // In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse + // the format and determine exactly how many digits are being requested and whether they + // represent "significant digits" or "digits after the decimal point". + static abstract int MaxPrecisionCustomFormat { get; } } internal static partial class Number diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs index eca30a3c3ea12..2f08b4e800016 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs @@ -306,7 +306,7 @@ static virtual TSelf Parse(ReadOnlySpan utf8Text, NumberStyles style, IFor scoped Span utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); - if (textMaxCharCount < 256) + if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; @@ -314,7 +314,7 @@ static virtual TSelf Parse(ReadOnlySpan utf8Text, NumberStyles style, IFor else { utf16TextArray = ArrayPool.Shared.Rent(textMaxCharCount); - utf16Text = utf16TextArray.AsSpan(0, textMaxCharCount); + utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); @@ -440,7 +440,7 @@ static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IF scoped Span utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); - if (textMaxCharCount < 256) + if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; @@ -448,7 +448,7 @@ static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IF else { utf16TextArray = ArrayPool.Shared.Rent(textMaxCharCount); - utf16Text = utf16TextArray.AsSpan(0, textMaxCharCount); + utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); @@ -486,7 +486,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri scoped Span utf16Destination; int destinationMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Destination.Length); - if (destinationMaxCharCount < 256) + if (destinationMaxCharCount <= 256) { utf16DestinationArray = null; utf16Destination = stackalloc char[256]; @@ -494,7 +494,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri else { utf16DestinationArray = ArrayPool.Shared.Rent(destinationMaxCharCount); - utf16Destination = utf16DestinationArray.AsSpan(0, destinationMaxCharCount); + utf16Destination = utf16DestinationArray; } if (!TryFormat(utf16Destination, out int charsWritten, format, provider)) @@ -542,7 +542,7 @@ static TSelf IUtf8SpanParsable.Parse(ReadOnlySpan utf8Text, IFormat scoped Span utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); - if (textMaxCharCount < 256) + if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; @@ -550,7 +550,7 @@ static TSelf IUtf8SpanParsable.Parse(ReadOnlySpan utf8Text, IFormat else { utf16TextArray = ArrayPool.Shared.Rent(textMaxCharCount); - utf16Text = utf16TextArray.AsSpan(0, textMaxCharCount); + utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); @@ -589,7 +589,7 @@ static bool IUtf8SpanParsable.TryParse(ReadOnlySpan utf8Text, IForm scoped Span utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); - if (textMaxCharCount < 256) + if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; @@ -597,7 +597,7 @@ static bool IUtf8SpanParsable.TryParse(ReadOnlySpan utf8Text, IForm else { utf16TextArray = ArrayPool.Shared.Rent(textMaxCharCount); - utf16Text = utf16TextArray.AsSpan(0, textMaxCharCount); + utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs index a4636a6e76a15..09d19a137f917 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs @@ -30,8 +30,7 @@ public struct Plane : IEquatable [Intrinsic] public Plane(float x, float y, float z, float d) { - Normal = new Vector3(x, y, z); - D = d; + this = Vector128.Create(x, y, z, d).AsPlane(); } /// Creates a object from a specified normal and the distance along the normal from the origin. @@ -40,8 +39,7 @@ public Plane(float x, float y, float z, float d) [Intrinsic] public Plane(Vector3 normal, float d) { - Normal = normal; - D = d; + this = new Vector4(normal, d).AsPlane(); } /// Creates a object from a specified four-dimensional vector. @@ -49,8 +47,7 @@ public Plane(Vector3 normal, float d) [Intrinsic] public Plane(Vector4 value) { - Normal = new Vector3(value.X, value.Y, value.Z); - D = value.W; + this = value.AsPlane(); } /// Creates a object that contains three specified points. @@ -111,52 +108,19 @@ public static Plane CreateFromVertices(Vector3 point1, Vector3 point2, Vector3 p /// The dot product. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(Plane plane, Vector4 value) - { - return (plane.Normal.X * value.X) - + (plane.Normal.Y * value.Y) - + (plane.Normal.Z * value.Z) - + (plane.D * value.W); - } + public static float Dot(Plane plane, Vector4 value) => Vector128.Dot(plane.AsVector128(), value.AsVector128()); /// Returns the dot product of a specified three-dimensional vector and the normal vector of this plane plus the distance () value of the plane. /// The plane. /// The 3-dimensional vector. /// The dot product. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DotCoordinate(Plane plane, Vector3 value) - { - if (Vector128.IsHardwareAccelerated) - { - return Vector3.Dot(plane.Normal, value) + plane.D; - } - else - { - return plane.Normal.X * value.X + - plane.Normal.Y * value.Y + - plane.Normal.Z * value.Z + - plane.D; - } - } + public static float DotCoordinate(Plane plane, Vector3 value) => Vector3.Dot(plane.Normal, value) + plane.D; /// Returns the dot product of a specified three-dimensional vector and the vector of this plane. /// The plane. /// The three-dimensional vector. /// The dot product. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DotNormal(Plane plane, Vector3 value) - { - if (Vector128.IsHardwareAccelerated) - { - return Vector3.Dot(plane.Normal, value); - } - else - { - return plane.Normal.X * value.X + - plane.Normal.Y * value.Y + - plane.Normal.Z * value.Z; - } - } + public static float DotNormal(Plane plane, Vector3 value) => Vector3.Dot(plane.Normal, value); /// Creates a new object whose normal vector is the source plane's normal vector normalized. /// The source plane. @@ -164,36 +128,15 @@ public static float DotNormal(Plane plane, Vector3 value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Plane Normalize(Plane value) { - if (Vector128.IsHardwareAccelerated) + float normalLengthSquared = value.Normal.LengthSquared(); + + if (MathF.Abs(normalLengthSquared - 1.0f) < NormalizeEpsilon) { - float normalLengthSquared = value.Normal.LengthSquared(); - if (MathF.Abs(normalLengthSquared - 1.0f) < NormalizeEpsilon) - { - // It already normalized, so we don't need to farther process. - return value; - } - float normalLength = MathF.Sqrt(normalLengthSquared); - return new Plane( - value.Normal / normalLength, - value.D / normalLength); + // It already normalized, so we don't need to farther process. + return value; } - else - { - float f = value.Normal.X * value.Normal.X + value.Normal.Y * value.Normal.Y + value.Normal.Z * value.Normal.Z; - if (MathF.Abs(f - 1.0f) < NormalizeEpsilon) - { - return value; // It already normalized, so we don't need to further process. - } - - float fInv = 1.0f / MathF.Sqrt(f); - - return new Plane( - value.Normal.X * fInv, - value.Normal.Y * fInv, - value.Normal.Z * fInv, - value.D * fInv); - } + return (value.AsVector128() / MathF.Sqrt(normalLengthSquared)).AsPlane(); } /// Transforms a normalized plane by a 4x4 matrix. @@ -267,11 +210,7 @@ public static Plane Transform(Plane plane, Quaternion rotation) /// The method defines the operation of the equality operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Plane value1, Plane value2) - { - return (value1.Normal == value2.Normal) - && (value1.D == value2.D); - } + public static bool operator ==(Plane value1, Plane value2) => value1.AsVector128() == value2.AsVector128(); /// Returns a value that indicates whether two planes are not equal. /// The first plane to compare. @@ -279,52 +218,24 @@ public static Plane Transform(Plane plane, Quaternion rotation) /// if and are not equal; otherwise, . /// The method defines the operation of the inequality operator for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Plane value1, Plane value2) - { - return !(value1 == value2); - } + public static bool operator !=(Plane value1, Plane value2) => !(value1 == value2); /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their and fields are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly bool Equals([NotNullWhen(true)] object? obj) - { - return (obj is Plane other) && Equals(other); - } + public override readonly bool Equals([NotNullWhen(true)] object? obj) => (obj is Plane other) && Equals(other); /// Returns a value that indicates whether this instance and another plane object are equal. /// The other plane. /// if the two planes are equal; otherwise, . /// Two objects are equal if their and fields are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Plane other) - { - // This function needs to account for floating-point equality around NaN - // and so must behave equivalently to the underlying float/double.Equals - - if (Vector128.IsHardwareAccelerated) - { - return this.AsVector128().Equals(other.AsVector128()); - } - - return SoftwareFallback(in this, other); - - static bool SoftwareFallback(in Plane self, Plane other) - { - return self.Normal.Equals(other.Normal) - && self.D.Equals(other.D); - } - } + public readonly bool Equals(Plane other) => this.AsVector128().Equals(other.AsVector128()); /// Returns the hash code for this instance. /// The hash code. - public override readonly int GetHashCode() - { - return HashCode.Combine(Normal, D); - } + public override readonly int GetHashCode() => HashCode.Combine(Normal, D); /// Returns the string representation of this plane object. /// A string that represents this object. diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.Extensions.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.Extensions.cs index 7c0e561c2edec..42ac2e3d0dfc3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.Extensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.Extensions.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace System.Numerics { @@ -17,12 +18,7 @@ public static unsafe partial class Vector [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static float GetElement(this Quaternion quaternion, int index) { - if ((uint)(index) >= (uint)(Quaternion.Count)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); - } - - return quaternion.GetElementUnsafe(index); + return quaternion.AsVector128().GetElement(index); } /// Creates a new with the element at the specified index set to the specified value and the remaining elements set to the same value as that in the given quaternion. @@ -34,14 +30,7 @@ internal static float GetElement(this Quaternion quaternion, int index) [Intrinsic] internal static Quaternion WithElement(this Quaternion quaternion, int index, float value) { - if ((uint)(index) >= (uint)(Quaternion.Count)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); - } - - Quaternion result = quaternion; - result.SetElementUnsafe(index, value); - return result; + return quaternion.AsVector128().WithElement(index, value).AsQuaternion(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 ecebdb1b3d42f..4ec7f536ed43f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs @@ -38,10 +38,7 @@ public struct Quaternion : IEquatable [Intrinsic] public Quaternion(float x, float y, float z, float w) { - X = x; - Y = y; - Z = z; - W = w; + this = Vector128.Create(x, y, z, w).AsQuaternion(); } /// Creates a quaternion from the specified vector and rotation parts. @@ -50,10 +47,7 @@ public Quaternion(float x, float y, float z, float w) [Intrinsic] public Quaternion(Vector3 vectorPart, float scalarPart) { - X = vectorPart.X; - Y = vectorPart.Y; - Z = vectorPart.Z; - W = scalarPart; + this = new Vector4(vectorPart, scalarPart).AsQuaternion(); } /// Gets a quaternion that represents a zero. @@ -79,20 +73,15 @@ public static Quaternion Identity public float this[int index] { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.GetElement(index); - [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this = this.WithElement(index, value); } /// Gets a value that indicates whether the current instance is the identity quaternion. /// if the current instance is the identity quaternion; otherwise, . /// - public readonly bool IsIdentity - { - get => this == Identity; - } + public readonly bool IsIdentity => this == Identity; /// Adds each element in one quaternion with its corresponding element in a second quaternion. /// The first quaternion. @@ -101,15 +90,7 @@ public readonly bool IsIdentity /// The method defines the operation of the addition operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion operator +(Quaternion value1, Quaternion value2) - { - return new Quaternion( - value1.X + value2.X, - value1.Y + value2.Y, - value1.Z + value2.Z, - value1.W + value2.W - ); - } + public static Quaternion operator +(Quaternion value1, Quaternion value2) => (value1.AsVector128() + value2.AsVector128()).AsQuaternion(); /// Divides one quaternion by a second quaternion. /// The dividend. @@ -162,24 +143,14 @@ public readonly bool IsIdentity /// The method defines the operation of the equality operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Quaternion value1, Quaternion value2) - { - return (value1.X == value2.X) - && (value1.Y == value2.Y) - && (value1.Z == value2.Z) - && (value1.W == value2.W); - } + public static bool operator ==(Quaternion value1, Quaternion value2) => value1.AsVector128() == value2.AsVector128(); /// Returns a value that indicates whether two quaternions are not equal. /// The first quaternion to compare. /// The second quaternion to compare. /// if and are not equal; otherwise, . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Quaternion value1, Quaternion value2) - { - return !(value1 == value2); - } + public static bool operator !=(Quaternion value1, Quaternion value2) => !(value1 == value2); /// Returns the quaternion that results from multiplying two quaternions together. /// The first quaternion. @@ -236,15 +207,7 @@ public readonly bool IsIdentity /// The method defines the operation of the multiplication operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion operator *(Quaternion value1, float value2) - { - return new Quaternion( - value1.X * value2, - value1.Y * value2, - value1.Z * value2, - value1.W * value2 - ); - } + public static Quaternion operator *(Quaternion value1, float value2) => (value1.AsVector128() * value2).AsQuaternion(); /// Subtracts each element in a second quaternion from its corresponding element in a first quaternion. /// The first quaternion. @@ -253,15 +216,7 @@ public readonly bool IsIdentity /// The method defines the operation of the subtraction operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion operator -(Quaternion value1, Quaternion value2) - { - return new Quaternion( - value1.X - value2.X, - value1.Y - value2.Y, - value1.Z - value2.Z, - value1.W - value2.W - ); - } + public static Quaternion operator -(Quaternion value1, Quaternion value2) => (value1.AsVector128() - value2.AsVector128()).AsQuaternion(); /// Reverses the sign of each component of the quaternion. /// The quaternion to negate. @@ -269,21 +224,14 @@ public readonly bool IsIdentity /// The method defines the operation of the unary negation operator for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion operator -(Quaternion value) - { - return Zero - value; - } + public static Quaternion operator -(Quaternion value) => (-value.AsVector128()).AsQuaternion(); /// Adds each element in one quaternion with its corresponding element in a second quaternion. /// The first quaternion. /// The second quaternion. /// The quaternion that contains the summed values of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Add(Quaternion value1, Quaternion value2) - { - return value1 + value2; - } + public static Quaternion Add(Quaternion value1, Quaternion value2) => value1 + value2; /// Concatenates two quaternions. /// The first quaternion rotation in the series. @@ -325,10 +273,7 @@ public static Quaternion Concatenate(Quaternion value1, Quaternion value2) /// A new quaternion that is the conjugate of . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Conjugate(Quaternion value) - { - return Multiply(value, new Vector4(-1.0f, -1.0f, -1.0f, 1.0f)); - } + public static Quaternion Conjugate(Quaternion value) => (value.AsVector128() * Vector128.Create(-1.0f, -1.0f, -1.0f, 1.0f)).AsQuaternion(); /// Creates a quaternion from a unit vector and an angle to rotate around the vector. /// The unit vector to rotate around. @@ -440,27 +385,7 @@ public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float ro /// The dividend. /// The divisor. /// The quaternion that results from dividing by . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Divide(Quaternion value1, Quaternion value2) - { - return value1 / value2; - } - - /// Divides the specified quaternion by a specified scalar value. - /// The quaternion. - /// The scalar value. - /// The quaternion that results from the division. - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Quaternion Divide(Quaternion left, float divisor) - { - return new Quaternion( - left.X / divisor, - left.Y / divisor, - left.Z / divisor, - left.W / divisor - ); - } + public static Quaternion Divide(Quaternion value1, Quaternion value2) => value1 / value2; /// Calculates the dot product of two quaternions. /// The first quaternion. @@ -468,13 +393,7 @@ internal static Quaternion Divide(Quaternion left, float divisor) /// The dot product. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(Quaternion quaternion1, Quaternion quaternion2) - { - return (quaternion1.X * quaternion2.X) - + (quaternion1.Y * quaternion2.Y) - + (quaternion1.Z * quaternion2.Z) - + (quaternion1.W * quaternion2.W); - } + public static float Dot(Quaternion quaternion1, Quaternion quaternion2) => Vector128.Dot(quaternion1.AsVector128(), quaternion2.AsVector128()); /// Returns the inverse of a quaternion. /// The quaternion. @@ -487,7 +406,7 @@ public static Quaternion Inverse(Quaternion value) // q = ( ------------- ------------- ) // ( a^2 + |v|^2 , a^2 + |v|^2 ) - return Divide(Conjugate(value), value.LengthSquared()); + return (Conjugate(value).AsVector128() / value.LengthSquared()).AsQuaternion(); } /// Performs a linear interpolation between two quaternions based on a value that specifies the weighting of the second quaternion. @@ -536,58 +455,27 @@ public static Quaternion Lerp(Quaternion quaternion1, Quaternion quaternion2, fl /// The first quaternion. /// The second quaternion. /// The product quaternion. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Multiply(Quaternion value1, Quaternion value2) - { - return value1 * value2; - } - - /// Returns a new quaternion whose values are the product of each pair of elements in specified quaternion and vector. - /// The quaternion. - /// The vector. - /// The element-wise product vector. - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Quaternion Multiply(Quaternion value1, Vector4 value2) - { - return new Quaternion( - value1.X * value2.X, - value1.Y * value2.Y, - value1.Z * value2.Z, - value1.W * value2.W - ); - } + public static Quaternion Multiply(Quaternion value1, Quaternion value2) => value1 * value2; /// Returns the quaternion that results from scaling all the components of a specified quaternion by a scalar factor. /// The source quaternion. /// The scalar value. /// The scaled quaternion. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Multiply(Quaternion value1, float value2) - { - return value1 * value2; - } + public static Quaternion Multiply(Quaternion value1, float value2) => value1 * value2; /// Reverses the sign of each component of the quaternion. /// The quaternion to negate. /// The negated quaternion. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Negate(Quaternion value) - { - return -value; - } + public static Quaternion Negate(Quaternion value) => -value; /// Divides each component of a specified by its length. /// The quaternion to normalize. /// The normalized quaternion. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Normalize(Quaternion value) - { - return Divide(value, value.Length()); - } + public static Quaternion Normalize(Quaternion value) => (value.AsVector128() / value.Length()).AsQuaternion(); /// Interpolates between two quaternions, using spherical linear interpolation. /// The first quaternion. @@ -643,77 +531,38 @@ public static Quaternion Slerp(Quaternion quaternion1, Quaternion quaternion2, f /// The second quaternion. /// The quaternion containing the values that result from subtracting each element in from its corresponding element in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion Subtract(Quaternion value1, Quaternion value2) - { - return value1 - value2; - } + public static Quaternion Subtract(Quaternion value1, Quaternion value2) => value1 - value2; /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and the corresponding components of each matrix are equal. - public override readonly bool Equals([NotNullWhen(true)] object? obj) - { - return (obj is Quaternion other) && Equals(other); - } + public override readonly bool Equals([NotNullWhen(true)] object? obj) => (obj is Quaternion other) && Equals(other); /// Returns a value that indicates whether this instance and another quaternion are equal. /// The other quaternion. /// if the two quaternions are equal; otherwise, . /// Two quaternions are equal if each of their corresponding components is equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Quaternion other) - { - // This function needs to account for floating-point equality around NaN - // and so must behave equivalently to the underlying float/double.Equals - - if (Vector128.IsHardwareAccelerated) - { - return this.AsVector128().Equals(other.AsVector128()); - } - - return SoftwareFallback(in this, other); - - static bool SoftwareFallback(in Quaternion self, Quaternion other) - { - return self.X.Equals(other.X) - && self.Y.Equals(other.Y) - && self.Z.Equals(other.Z) - && self.W.Equals(other.W); - } - } + public readonly bool Equals(Quaternion other) => this.AsVector128().Equals(other.AsVector128()); /// Returns the hash code for this instance. /// The hash code. - public override readonly int GetHashCode() - { - return HashCode.Combine(X, Y, Z, W); - } + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z, W); /// Calculates the length of the quaternion. /// The computed length of the quaternion. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Length() - { - float lengthSquared = LengthSquared(); - return MathF.Sqrt(lengthSquared); - } + public readonly float Length() => MathF.Sqrt(LengthSquared()); /// Calculates the squared length of the quaternion. /// The length squared of the quaternion. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float LengthSquared() - { - return Dot(this, this); - } + public readonly float LengthSquared() => Dot(this, this); /// Returns a string that represents this quaternion. /// The string representation of this quaternion. /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as {X:1.1 Y:2.2 Z:3.3 W:4.4}. - public override readonly string ToString() => - $"{{X:{X} Y:{Y} Z:{Z} W:{W}}}"; + public override readonly string ToString() => $"{{X:{X} Y:{Y} Z:{Z} W:{W}}}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index 6bbdf2f742306..41961bbb54c79 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +using static Interop; namespace System.Numerics { @@ -59,7 +60,6 @@ public static Vector Abs(Vector value) /// The type of the elements in the vector. /// The sum of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Add(Vector left, Vector right) => left + right; /// Computes the bitwise-and of a given vector and the ones complement of another vector. @@ -68,7 +68,6 @@ public static Vector Abs(Vector value) /// The type of the elements in the vector. /// The bitwise-and of and the ones-complement of . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AndNot(Vector left, Vector right) => left & ~right; /// Reinterprets a as a new . @@ -84,7 +83,11 @@ public static Vector As(this Vector vector) ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); +#if MONO return Unsafe.As, Vector>(ref vector); +#else + return Unsafe.BitCast, Vector>(vector); +#endif } /// Reinterprets a as a new . @@ -93,7 +96,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorByte(Vector value) => value.As(); /// Reinterprets a as a new . @@ -102,7 +104,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorDouble(Vector value) => value.As(); /// Reinterprets a as a new . @@ -111,7 +112,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorInt16(Vector value) => value.As(); /// Reinterprets a as a new . @@ -120,7 +120,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorInt32(Vector value) => value.As(); /// Reinterprets a as a new . @@ -129,7 +128,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorInt64(Vector value) => value.As(); /// Reinterprets a as a new . @@ -138,7 +136,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorNInt(Vector value) => value.As(); /// Reinterprets a as a new . @@ -148,7 +145,6 @@ public static Vector As(this Vector vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorNUInt(Vector value) => value.As(); /// Reinterprets a as a new . @@ -158,7 +154,6 @@ public static Vector As(this Vector vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorSByte(Vector value) => value.As(); /// Reinterprets a as a new . @@ -167,7 +162,6 @@ public static Vector As(this Vector vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorSingle(Vector value) => value.As(); /// Reinterprets a as a new . @@ -177,7 +171,6 @@ public static Vector As(this Vector vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorUInt16(Vector value) => value.As(); /// Reinterprets a as a new . @@ -187,7 +180,6 @@ public static Vector As(this Vector vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorUInt32(Vector value) => value.As(); /// Reinterprets a as a new . @@ -197,7 +189,6 @@ public static Vector As(this Vector vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector AsVectorUInt64(Vector value) => value.As(); /// Computes the bitwise-and of two vectors. @@ -206,7 +197,6 @@ public static Vector As(this Vector vector) /// The type of the elements in the vector. /// The bitwise-and of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector BitwiseAnd(Vector left, Vector right) => left & right; /// Computes the bitwise-or of two vectors. @@ -215,7 +205,6 @@ public static Vector As(this Vector vector) /// The type of the elements in the vector. /// The bitwise-or of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector BitwiseOr(Vector left, Vector right) => left | right; /// Computes the ceiling of each element in a vector. @@ -272,9 +261,7 @@ public static Vector Ceiling(Vector value) /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) - => ConditionalSelect(condition.As(), left, right); + public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) => ConditionalSelect(condition.As(), left, right); /// Conditionally selects a value from two vectors on a bitwise basis. /// The mask that is used to select a value from or . @@ -282,9 +269,7 @@ public static Vector ConditionalSelect(Vector condition, VectorThe vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) - => ConditionalSelect(condition.As(), left, right); + public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) => ConditionalSelect(condition.As(), left, right); /// Converts a to a . /// The vector to convert. @@ -517,7 +502,6 @@ public static Vector ConvertToUInt64Native(Vector value) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Divide(Vector left, Vector right) => left / right; /// Divides a vector by a scalar to compute the per-element quotient. @@ -526,7 +510,6 @@ public static Vector ConvertToUInt64Native(Vector value) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Divide(Vector left, T right) => left / right; /// Computes the dot product of two vectors. @@ -535,19 +518,7 @@ public static Vector ConvertToUInt64Native(Vector value) /// The type of the elements in the vector. /// The dot product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Dot(Vector left, Vector right) - { - T result = default!; - - for (int index = 0; index < Vector.Count; index++) - { - T value = Scalar.Multiply(left.GetElementUnsafe(index), right.GetElementUnsafe(index)); - result = Scalar.Add(result, value); - } - - return result; - } + public static T Dot(Vector left, Vector right) => Sum(left * right); /// Compares two vectors to determine if they are equal on a per-element basis. /// The vector to compare with . @@ -574,7 +545,6 @@ public static Vector Equals(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if the corresponding elements in and were equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Equals(Vector left, Vector right) => Equals(left, right).As(); /// Compares two vectors to determine if they are equal on a per-element basis. @@ -582,7 +552,6 @@ public static Vector Equals(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if the corresponding elements in and were equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Equals(Vector left, Vector right) => Equals(left, right); /// Compares two vectors to determine if they are equal on a per-element basis. @@ -590,7 +559,6 @@ public static Vector Equals(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if the corresponding elements in and were equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Equals(Vector left, Vector right) => Equals(left, right); /// Compares two vectors to determine if they are equal on a per-element basis. @@ -598,7 +566,6 @@ public static Vector Equals(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if the corresponding elements in and were equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Equals(Vector left, Vector right) => Equals(left, right).As(); /// Compares two vectors to determine if all elements are equal. @@ -607,7 +574,6 @@ public static Vector Equals(Vector left, Vector right) /// The type of the elements in the vector. /// true if all elements in were equal to the corresponding element in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsAll(Vector left, Vector right) => left == right; /// Compares two vectors to determine if any elements are equal. @@ -760,7 +726,6 @@ public static Vector GreaterThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThan(Vector left, Vector right) => GreaterThan(left, right).As(); /// Compares two vectors to determine which is greater on a per-element basis. @@ -768,14 +733,13 @@ public static Vector GreaterThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThan(Vector left, Vector right) => GreaterThan(left, right); /// Compares two vectors to determine which is greater on a per-element basis. /// The vector to compare with . /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector GreaterThan(Vector left, Vector right) => GreaterThan(left, right); /// Compares two vectors to determine which is greater on a per-element basis. @@ -783,7 +747,6 @@ public static Vector GreaterThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThan(Vector left, Vector right) => GreaterThan(left, right).As(); /// Compares two vectors to determine if all elements are greater. @@ -851,7 +814,6 @@ public static Vector GreaterThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThanOrEqual(Vector left, Vector right) => GreaterThanOrEqual(left, right).As(); /// Compares two vectors to determine which is greater or equal on a per-element basis. @@ -859,7 +821,6 @@ public static Vector GreaterThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThanOrEqual(Vector left, Vector right) => GreaterThanOrEqual(left, right); /// Compares two vectors to determine which is greater or equal on a per-element basis. @@ -867,7 +828,6 @@ public static Vector GreaterThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThanOrEqual(Vector left, Vector right) => GreaterThanOrEqual(left, right); /// Compares two vectors to determine which is greater or equal on a per-element basis. @@ -875,7 +835,6 @@ public static Vector GreaterThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were greater or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector GreaterThanOrEqual(Vector left, Vector right) => GreaterThanOrEqual(left, right).As(); /// Compares two vectors to determine if all elements are greater or equal. @@ -943,7 +902,6 @@ public static Vector LessThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThan(Vector left, Vector right) => LessThan(left, right).As(); /// Compares two vectors to determine which is less on a per-element basis. @@ -951,7 +909,6 @@ public static Vector LessThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThan(Vector left, Vector right) => LessThan(left, right); /// Compares two vectors to determine which is less on a per-element basis. @@ -959,7 +916,6 @@ public static Vector LessThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThan(Vector left, Vector right) => LessThan(left, right); /// Compares two vectors to determine which is less on a per-element basis. @@ -967,7 +923,6 @@ public static Vector LessThan(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThan(Vector left, Vector right) => LessThan(left, right).As(); /// Compares two vectors to determine if all elements are less. @@ -1035,7 +990,6 @@ public static Vector LessThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThanOrEqual(Vector left, Vector right) => LessThanOrEqual(left, right).As(); /// Compares two vectors to determine which is less or equal on a per-element basis. @@ -1043,7 +997,6 @@ public static Vector LessThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThanOrEqual(Vector left, Vector right) => LessThanOrEqual(left, right); /// Compares two vectors to determine which is less or equal on a per-element basis. @@ -1051,7 +1004,6 @@ public static Vector LessThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThanOrEqual(Vector left, Vector right) => LessThanOrEqual(left, right); /// Compares two vectors to determine which is less or equal on a per-element basis. @@ -1059,7 +1011,6 @@ public static Vector LessThanOrEqual(Vector left, Vector right) /// The vector to compare with . /// A vector whose elements are all-bits-set or zero, depending on if which of the corresponding elements in and were less or equal. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LessThanOrEqual(Vector left, Vector right) => LessThanOrEqual(left, right).As(); /// Compares two vectors to determine if all elements are less or equal. @@ -1110,7 +1061,6 @@ public static bool LessThanOrEqualAny(Vector left, Vector right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. @@ -1141,7 +1091,6 @@ public static Vector LoadAligned(T* source) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector LoadAlignedNonTemporal(T* source) => LoadAligned(source); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1221,7 +1170,6 @@ public static Vector Min(Vector left, Vector right) /// The type of the elements in the vector. /// The element-wise product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Multiply(Vector left, Vector right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -1230,7 +1178,6 @@ public static Vector Min(Vector left, Vector right) /// The type of the elements in the vector. /// The product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Multiply(Vector left, T right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -1239,7 +1186,6 @@ public static Vector Min(Vector left, Vector right) /// The type of the elements in the vector. /// The product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Multiply(T left, Vector right) => left * right; /// @@ -1458,7 +1404,6 @@ public static Vector Narrow(Vector low, Vector high) /// The type of the elements in the vector. /// A vector whose elements are the unary negation of the corresponding elements in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Negate(Vector value) => -value; /// Computes the ones-complement of a vector. @@ -1466,7 +1411,6 @@ public static Vector Narrow(Vector low, Vector high) /// The type of the elements in the vector. /// A vector whose elements are the ones-complement of the corresponding elements in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector OnesComplement(Vector value) => ~value; /// Shifts each element of a vector left by the specified amount. @@ -1474,7 +1418,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1482,7 +1425,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1490,7 +1432,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1498,7 +1439,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1506,7 +1446,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1515,7 +1454,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1524,7 +1462,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1533,7 +1470,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1542,7 +1478,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -1551,7 +1486,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftLeft(Vector value, int shiftCount) => value << shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -1559,7 +1493,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightArithmetic(Vector value, int shiftCount) => value >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -1567,7 +1500,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightArithmetic(Vector value, int shiftCount) => value >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -1575,7 +1507,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightArithmetic(Vector value, int shiftCount) => value >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -1583,7 +1514,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightArithmetic(Vector value, int shiftCount) => value >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -1592,7 +1522,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightArithmetic(Vector value, int shiftCount) => value >> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1600,7 +1529,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1608,7 +1536,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1616,7 +1543,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1624,7 +1550,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1632,7 +1557,6 @@ public static Vector Narrow(Vector low, Vector high) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1641,7 +1565,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1650,7 +1573,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1659,7 +1581,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1668,7 +1589,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -1677,7 +1597,6 @@ public static Vector Narrow(Vector low, Vector high) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ShiftRightLogical(Vector value, int shiftCount) => value >>> shiftCount; /// Computes the square root of a vector on a per-element basis. @@ -1707,7 +1626,6 @@ public static Vector SquareRoot(Vector value) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Store(this Vector source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. @@ -1738,7 +1656,6 @@ public static void StoreAligned(this Vector source, T* destination) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StoreAlignedNonTemporal(this Vector source, T* destination) => source.StoreAligned(destination); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1778,7 +1695,6 @@ public static void StoreUnsafe(this Vector source, ref T destination, nuin /// The type of the elements in the vector. /// The difference of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Subtract(Vector left, Vector right) => left - right; /// @@ -1804,7 +1720,6 @@ public static T Sum(Vector value) /// A scalar containing the value of the first element. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ToScalar(this Vector vector) { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -2179,7 +2094,6 @@ public static Vector WithElement(this Vector vector, int index, T value /// The type of the elements in the vector. /// The exclusive-or of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector Xor(Vector left, Vector right) => left ^ right; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs index 2b27fe4f961e3..931cbf5eca86b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs @@ -93,10 +93,8 @@ public static Vector2 UnitY public float this[int index] { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.GetElement(index); - [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this = this.WithElement(index, value); } @@ -136,11 +134,7 @@ public float this[int index] /// The result of the division. /// The method defines the division operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator /(Vector2 value1, float value2) - { - return value1 / new Vector2(value2); - } + public static Vector2 operator /(Vector2 value1, float value2) => value1 / new Vector2(value2); /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. @@ -160,11 +154,7 @@ public float this[int index] /// The second vector to compare. /// if and are not equal; otherwise, . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector2 left, Vector2 right) - { - return !(left == right); - } + public static bool operator !=(Vector2 left, Vector2 right) => !(left == right); /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. @@ -187,11 +177,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(Vector2 left, float right) - { - return left * new Vector2(right); - } + public static Vector2 operator *(Vector2 left, float right) => left * new Vector2(right); /// Multiplies the scalar value by the specified vector. /// The vector. @@ -199,11 +185,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator *(float left, Vector2 right) - { - return right * left; - } + public static Vector2 operator *(float left, Vector2 right) => right * left; /// Subtracts the second vector from the first. /// The first vector. @@ -225,11 +207,7 @@ public float this[int index] /// The negated vector. /// The method defines the unary negation operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 operator -(Vector2 value) - { - return Zero - value; - } + public static Vector2 operator -(Vector2 value) => Zero - value; /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. @@ -249,11 +227,7 @@ public static Vector2 Abs(Vector2 value) /// The second vector to add. /// The summed vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Add(Vector2 left, Vector2 right) - { - return left + right; - } + public static Vector2 Add(Vector2 left, Vector2 right) => left + right; /// Restricts a vector between a minimum and a maximum value. /// The vector to restrict. @@ -261,7 +235,6 @@ public static Vector2 Add(Vector2 left, Vector2 right) /// The maximum value. /// The restricted vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max) { // We must follow HLSL behavior in the case user specified min value is bigger than max value. @@ -273,46 +246,28 @@ public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max) /// The second point. /// The distance. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Distance(Vector2 value1, Vector2 value2) - { - float distanceSquared = DistanceSquared(value1, value2); - return MathF.Sqrt(distanceSquared); - } + public static float Distance(Vector2 value1, Vector2 value2) => MathF.Sqrt(DistanceSquared(value1, value2)); /// Returns the Euclidean distance squared between two specified points. /// The first point. /// The second point. /// The distance squared. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistanceSquared(Vector2 value1, Vector2 value2) - { - Vector2 difference = value1 - value2; - return Dot(difference, difference); - } + public static float DistanceSquared(Vector2 value1, Vector2 value2) => (value1 - value2).LengthSquared(); /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector resulting from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Divide(Vector2 left, Vector2 right) - { - return left / right; - } + public static Vector2 Divide(Vector2 left, Vector2 right) => left / right; /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The vector that results from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Divide(Vector2 left, float divisor) - { - return left / divisor; - } + public static Vector2 Divide(Vector2 left, float divisor) => left / divisor; /// Returns the dot product of two vectors. /// The first vector. @@ -347,10 +302,7 @@ public static Vector2 FusedMultiplyAdd(Vector2 left, Vector2 right, Vector2 adde /// ]]> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount) - { - return (value1 * (1.0f - amount)) + (value2 * amount); - } + public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount) => (value1 * (1.0f - amount)) + (value2 * amount); /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. /// The first vector. @@ -385,33 +337,21 @@ public static Vector2 Min(Vector2 value1, Vector2 value2) /// The second vector. /// The element-wise product vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Multiply(Vector2 left, Vector2 right) - { - return left * right; - } + public static Vector2 Multiply(Vector2 left, Vector2 right) => left * right; /// Multiplies a vector by a specified scalar. /// The vector to multiply. /// The scalar value. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Multiply(Vector2 left, float right) - { - return left * right; - } + public static Vector2 Multiply(Vector2 left, float right) => left * right; /// Multiplies a scalar value by a specified vector. /// The scaled value. /// The vector. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Multiply(float left, Vector2 right) - { - return left * right; - } + public static Vector2 Multiply(float left, Vector2 right) => left * right; /// [Intrinsic] @@ -428,32 +368,20 @@ public static Vector2 MultiplyAddEstimate(Vector2 left, Vector2 right, Vector2 a /// The vector to negate. /// The negated vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Negate(Vector2 value) - { - return -value; - } + public static Vector2 Negate(Vector2 value) => -value; /// Returns a vector with the same direction as the specified vector, but with a length of one. /// The vector to normalize. /// The normalized vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Normalize(Vector2 value) - { - return value / value.Length(); - } + public static Vector2 Normalize(Vector2 value) => value / value.Length(); /// Returns the reflection of a vector off a surface that has the specified normal. /// The source vector. /// The normal of the surface being reflected off. /// The reflected vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Reflect(Vector2 vector, Vector2 normal) - { - float dot = Dot(vector, normal); - return vector - (2.0f * (dot * normal)); - } + public static Vector2 Reflect(Vector2 vector, Vector2 normal) => vector - (2.0f * (Dot(vector, normal) * normal)); /// Returns a vector whose elements are the square root of each of a specified vector's elements. /// A vector. @@ -473,21 +401,13 @@ public static Vector2 SquareRoot(Vector2 value) /// The second vector. /// The difference vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Subtract(Vector2 left, Vector2 right) - { - return left - right; - } + public static Vector2 Subtract(Vector2 left, Vector2 right) => left - right; /// Transforms a vector by a specified 3x2 matrix. /// The vector to transform. /// The transformation matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Transform(Vector2 position, Matrix3x2 matrix) - { - return Transform(position, in matrix.AsImpl()); - } + public static Vector2 Transform(Vector2 position, Matrix3x2 matrix) => Transform(position, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector2 Transform(Vector2 position, in Matrix3x2.Impl matrix) @@ -505,10 +425,7 @@ internal static Vector2 Transform(Vector2 position, in Matrix3x2.Impl matrix) /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 Transform(Vector2 position, Matrix4x4 matrix) - { - return Vector4.Transform(position, in matrix.AsImpl()).AsVector128().AsVector2(); - } + public static Vector2 Transform(Vector2 position, Matrix4x4 matrix) => Vector4.Transform(position, in matrix.AsImpl()).AsVector128().AsVector2(); /// Transforms a vector by the specified Quaternion rotation value. /// The vector to rotate. @@ -537,11 +454,7 @@ public static Vector2 Transform(Vector2 value, Quaternion rotation) /// The source vector. /// The matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 TransformNormal(Vector2 normal, Matrix3x2 matrix) - { - return TransformNormal(normal, in matrix.AsImpl()); - } + public static Vector2 TransformNormal(Vector2 normal, Matrix3x2 matrix) => TransformNormal(normal, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector2 TransformNormal(Vector2 normal, in Matrix3x2.Impl matrix) @@ -557,11 +470,7 @@ internal static Vector2 TransformNormal(Vector2 normal, in Matrix3x2.Impl matrix /// The source vector. /// The matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 TransformNormal(Vector2 normal, Matrix4x4 matrix) - { - return TransformNormal(normal, in matrix.AsImpl()); - } + public static Vector2 TransformNormal(Vector2 normal, Matrix4x4 matrix) => TransformNormal(normal, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector2 TransformNormal(Vector2 normal, in Matrix4x4.Impl matrix) @@ -653,11 +562,7 @@ public readonly bool TryCopyTo(Span destination) /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their and elements are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly bool Equals([NotNullWhen(true)] object? obj) - { - return (obj is Vector2 other) && Equals(other); - } + public override readonly bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector2 other) && Equals(other); /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. @@ -689,40 +594,25 @@ static bool SoftwareFallback(in Vector2 self, Vector2 other) /// Returns the hash code for this instance. /// The hash code. - public override readonly int GetHashCode() - { - return HashCode.Combine(X, Y); - } + public override readonly int GetHashCode() => HashCode.Combine(X, Y); /// Returns the length of the vector. /// The vector's length. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Length() - { - float lengthSquared = LengthSquared(); - return MathF.Sqrt(lengthSquared); - } + public readonly float Length() => MathF.Sqrt(LengthSquared()); /// Returns the length of the vector squared. /// The vector's length squared. /// This operation offers better performance than a call to the method. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float LengthSquared() - { - return Dot(this, this); - } + public readonly float LengthSquared() => Dot(this, this); /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. - public override readonly string ToString() - { - return ToString("G", CultureInfo.CurrentCulture); - } + public override readonly string ToString() => ToString("G", CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. @@ -730,10 +620,7 @@ public override readonly string ToString() /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings - public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) - { - return ToString(format, CultureInfo.CurrentCulture); - } + public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) => ToString(format, CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs index 382b6838193bd..7dc4994040eaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs @@ -114,10 +114,8 @@ public static Vector3 UnitZ public float this[int index] { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.GetElement(index); - [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this = this.WithElement(index, value); } @@ -159,11 +157,7 @@ public float this[int index] /// The result of the division. /// The method defines the division operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 operator /(Vector3 value1, float value2) - { - return value1 / new Vector3(value2); - } + public static Vector3 operator /(Vector3 value1, float value2) => value1 / new Vector3(value2); /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. @@ -184,11 +178,7 @@ public float this[int index] /// The second vector to compare. /// if and are not equal; otherwise, . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector3 left, Vector3 right) - { - return !(left == right); - } + public static bool operator !=(Vector3 left, Vector3 right) => !(left == right); /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. @@ -212,11 +202,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 operator *(Vector3 left, float right) - { - return left * new Vector3(right); - } + public static Vector3 operator *(Vector3 left, float right) => left * new Vector3(right); /// Multiplies the scalar value by the specified vector. /// The vector. @@ -224,11 +210,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 operator *(float left, Vector3 right) - { - return right * left; - } + public static Vector3 operator *(float left, Vector3 right) => right * left; /// Subtracts the second vector from the first. /// The first vector. @@ -251,11 +233,7 @@ public float this[int index] /// The negated vector. /// The method defines the unary negation operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 operator -(Vector3 value) - { - return Zero - value; - } + public static Vector3 operator -(Vector3 value) => Zero - value; /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. @@ -276,11 +254,7 @@ public static Vector3 Abs(Vector3 value) /// The second vector to add. /// The summed vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Add(Vector3 left, Vector3 right) - { - return left + right; - } + public static Vector3 Add(Vector3 left, Vector3 right) => left + right; /// Restricts a vector between a minimum and a maximum value. /// The vector to restrict. @@ -288,7 +262,6 @@ public static Vector3 Add(Vector3 left, Vector3 right) /// The maximum value. /// The restricted vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 max) { // We must follow HLSL behavior in the case user specified min value is bigger than max value. @@ -314,46 +287,28 @@ public static Vector3 Cross(Vector3 vector1, Vector3 vector2) /// The second point. /// The distance. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Distance(Vector3 value1, Vector3 value2) - { - float distanceSquared = DistanceSquared(value1, value2); - return MathF.Sqrt(distanceSquared); - } + public static float Distance(Vector3 value1, Vector3 value2) => MathF.Sqrt(DistanceSquared(value1, value2)); /// Returns the Euclidean distance squared between two specified points. /// The first point. /// The second point. /// The distance squared. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistanceSquared(Vector3 value1, Vector3 value2) - { - Vector3 difference = value1 - value2; - return Dot(difference, difference); - } + public static float DistanceSquared(Vector3 value1, Vector3 value2) => (value1 - value2).LengthSquared(); /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector resulting from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Divide(Vector3 left, Vector3 right) - { - return left / right; - } + public static Vector3 Divide(Vector3 left, Vector3 right) => left / right; /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The vector that results from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Divide(Vector3 left, float divisor) - { - return left / divisor; - } + public static Vector3 Divide(Vector3 left, float divisor) => left / divisor; /// Returns the dot product of two vectors. /// The first vector. @@ -387,10 +342,7 @@ public static Vector3 FusedMultiplyAdd(Vector3 left, Vector3 right, Vector3 adde /// The interpolated vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Lerp(Vector3 value1, Vector3 value2, float amount) - { - return (value1 * (1.0f - amount)) + (value2 * amount); - } + public static Vector3 Lerp(Vector3 value1, Vector3 value2, float amount) => (value1 * (1.0f - amount)) + (value2 * amount); /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. /// The first vector. @@ -427,33 +379,21 @@ public static Vector3 Min(Vector3 value1, Vector3 value2) /// The second vector. /// The element-wise product vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Multiply(Vector3 left, Vector3 right) - { - return left * right; - } + public static Vector3 Multiply(Vector3 left, Vector3 right) => left * right; /// Multiplies a vector by a specified scalar. /// The vector to multiply. /// The scalar value. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Multiply(Vector3 left, float right) - { - return left * right; - } + public static Vector3 Multiply(Vector3 left, float right) => left * right; /// Multiplies a scalar value by a specified vector. /// The scaled value. /// The vector. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Multiply(float left, Vector3 right) - { - return left * right; - } + public static Vector3 Multiply(float left, Vector3 right) => left * right; /// [Intrinsic] @@ -471,32 +411,20 @@ public static Vector3 MultiplyAddEstimate(Vector3 left, Vector3 right, Vector3 a /// The vector to negate. /// The negated vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Negate(Vector3 value) - { - return -value; - } + public static Vector3 Negate(Vector3 value) => -value; /// Returns a vector with the same direction as the specified vector, but with a length of one. /// The vector to normalize. /// The normalized vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Normalize(Vector3 value) - { - return value / value.Length(); - } + public static Vector3 Normalize(Vector3 value) => value / value.Length(); /// Returns the reflection of a vector off a surface that has the specified normal. /// The source vector. /// The normal of the surface being reflected off. /// The reflected vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Reflect(Vector3 vector, Vector3 normal) - { - float dot = Dot(vector, normal); - return vector - (2.0f * (dot * normal)); - } + public static Vector3 Reflect(Vector3 vector, Vector3 normal) => vector - (2.0f * (Dot(vector, normal) * normal)); /// Returns a vector whose elements are the square root of each of a specified vector's elements. /// A vector. @@ -517,21 +445,14 @@ public static Vector3 SquareRoot(Vector3 value) /// The second vector. /// The difference vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Subtract(Vector3 left, Vector3 right) - { - return left - right; - } + public static Vector3 Subtract(Vector3 left, Vector3 right) => left - right; /// Transforms a vector by a specified 4x4 matrix. /// The vector to transform. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 Transform(Vector3 position, Matrix4x4 matrix) - { - return Vector4.Transform(position, in matrix.AsImpl()).AsVector128().AsVector3(); - } + public static Vector3 Transform(Vector3 position, Matrix4x4 matrix) => Vector4.Transform(position, in matrix.AsImpl()).AsVector128().AsVector3(); /// Transforms a vector by the specified Quaternion rotation value. /// The vector to rotate. @@ -566,10 +487,7 @@ public static Vector3 Transform(Vector3 value, Quaternion rotation) /// The matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector3 TransformNormal(Vector3 normal, Matrix4x4 matrix) - { - return TransformNormal(normal, in matrix.AsImpl()); - } + public static Vector3 TransformNormal(Vector3 normal, Matrix4x4 matrix) => TransformNormal(normal, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector3 TransformNormal(Vector3 normal, in Matrix4x4.Impl matrix) @@ -662,11 +580,7 @@ public readonly bool TryCopyTo(Span destination) /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their corresponding elements are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly bool Equals([NotNullWhen(true)] object? obj) - { - return (obj is Vector3 other) && Equals(other); - } + public override readonly bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector3 other) && Equals(other); /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. @@ -695,40 +609,25 @@ static bool SoftwareFallback(in Vector3 self, Vector3 other) /// Returns the hash code for this instance. /// The hash code. - public override readonly int GetHashCode() - { - return HashCode.Combine(X, Y, Z); - } + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z); /// Returns the length of this vector object. /// The vector's length. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Length() - { - float lengthSquared = LengthSquared(); - return MathF.Sqrt(lengthSquared); - } + public readonly float Length() => MathF.Sqrt(LengthSquared()); /// Returns the length of the vector squared. /// The vector's length squared. /// This operation offers better performance than a call to the method. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float LengthSquared() - { - return Dot(this, this); - } + public readonly float LengthSquared() => Dot(this, this); /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. - public override readonly string ToString() - { - return ToString("G", CultureInfo.CurrentCulture); - } + public override readonly string ToString() => ToString("G", CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. @@ -736,10 +635,7 @@ public override readonly string ToString() /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings - public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) - { - return ToString(format, CultureInfo.CurrentCulture); - } + public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) => ToString(format, CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.Extensions.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.Extensions.cs index 143d003b96022..5e276425a0e53 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.Extensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.Extensions.cs @@ -3,27 +3,43 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace System.Numerics { public static unsafe partial class Vector { + /// Reinterprets a as a new . + /// The vector to reinterpret. + /// reinterpreted as a new . + internal static Plane AsPlane(this Vector4 value) + { +#if MONO + return Unsafe.As(ref value); +#else + return Unsafe.BitCast(value); +#endif + } + + /// Reinterprets a as a new . + /// The vector to reinterpret. + /// reinterpreted as a new . + internal static Quaternion AsQuaternion(this Vector4 value) + { +#if MONO + return Unsafe.As(ref value); +#else + return Unsafe.BitCast(value); +#endif + } + /// Gets the element at the specified index. /// The vector to get the element from. /// The index of the element to get. /// The value of the element at . /// was less than zero or greater than the number of elements. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static float GetElement(this Vector4 vector, int index) - { - if ((uint)(index) >= (uint)(Vector4.Count)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); - } - - return vector.GetElementUnsafe(index); - } + internal static float GetElement(this Vector4 vector, int index) => vector.AsVector128().GetElement(index); /// Creates a new with the element at the specified index set to the specified value and the remaining elements set to the same value as that in the given vector. /// The vector to get the remaining elements from. @@ -32,17 +48,8 @@ internal static float GetElement(this Vector4 vector, int index) /// A with the value of the element at set to and the remaining elements set to the same value as that in . /// was less than zero or greater than the number of elements. [Intrinsic] - internal static Vector4 WithElement(this Vector4 vector, int index, float value) - { - if ((uint)(index) >= (uint)(Vector4.Count)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index); - } - - Vector4 result = vector; - result.SetElementUnsafe(index, value); - return result; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 WithElement(this Vector4 vector, int index, float value) => vector.AsVector128().WithElement(index, value).AsVector4(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetElementUnsafe(in this Vector4 vector, int index) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs index 6a1a1c39c869c..f6893396cc4fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -34,8 +35,9 @@ public partial struct Vector4 : IEquatable, IFormattable /// Creates a new object whose four elements have the same value. /// The value to assign to all four elements. [Intrinsic] - public Vector4(float value) : this(value, value, value, value) + public Vector4(float value) { + this = Vector128.Create(value).AsVector4(); } /// Creates a new object from the specified object and a Z and a W component. @@ -43,16 +45,23 @@ public Vector4(float value) : this(value, value, value, value) /// The Z component. /// The W component. [Intrinsic] - public Vector4(Vector2 value, float z, float w) : this(value.X, value.Y, z, w) + public Vector4(Vector2 value, float z, float w) { + this = value.AsVector128() + .WithElement(2, z) + .WithElement(3, w) + .AsVector4(); } /// Constructs a new object from the specified object and a W component. /// The vector to use for the X, Y, and Z components. /// The W component. [Intrinsic] - public Vector4(Vector3 value, float w) : this(value.X, value.Y, value.Z, w) + public Vector4(Vector3 value, float w) { + this = value.AsVector128() + .WithElement(3, w) + .AsVector4(); } /// Creates a vector whose elements have the specified values. @@ -63,22 +72,14 @@ public Vector4(Vector3 value, float w) : this(value.X, value.Y, value.Z, w) [Intrinsic] public Vector4(float x, float y, float z, float w) { - X = x; - Y = y; - Z = z; - W = w; + this = Vector128.Create(x, y, z, w).AsVector4(); } /// Constructs a vector from the given . The span must contain at least 4 elements. /// The span of elements to assign to the vector. public Vector4(ReadOnlySpan values) { - if (values.Length < 4) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.values); - } - - this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + this = Vector128.Create(values).AsVector4(); } /// Gets a vector whose 4 elements are equal to zero. @@ -95,7 +96,7 @@ public static Vector4 Zero public static Vector4 One { [Intrinsic] - get => new Vector4(1.0f); + get => new Vector4(1); } /// Gets the vector (1,0,0,0). @@ -137,10 +138,8 @@ public static Vector4 UnitW public float this[int index] { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.GetElement(index); - [MethodImpl(MethodImplOptions.AggressiveInlining)] set => this = this.WithElement(index, value); } @@ -151,15 +150,7 @@ public float this[int index] /// The method defines the addition operation for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator +(Vector4 left, Vector4 right) - { - return new Vector4( - left.X + right.X, - left.Y + right.Y, - left.Z + right.Z, - left.W + right.W - ); - } + public static Vector4 operator +(Vector4 left, Vector4 right) => (left.AsVector128() + right.AsVector128()).AsVector4(); /// Divides the first vector by the second. /// The first vector. @@ -168,15 +159,7 @@ public float this[int index] /// The method defines the division operation for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator /(Vector4 left, Vector4 right) - { - return new Vector4( - left.X / right.X, - left.Y / right.Y, - left.Z / right.Z, - left.W / right.W - ); - } + public static Vector4 operator /(Vector4 left, Vector4 right) => (left.AsVector128() / right.AsVector128()).AsVector4(); /// Divides the specified vector by a specified scalar value. /// The vector. @@ -184,11 +167,7 @@ public float this[int index] /// The result of the division. /// The method defines the division operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator /(Vector4 value1, float value2) - { - return value1 / new Vector4(value2); - } + public static Vector4 operator /(Vector4 value1, float value2) => value1 / new Vector4(value2); /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. /// The first vector to compare. @@ -197,24 +176,14 @@ public float this[int index] /// Two objects are equal if each element in is equal to the corresponding element in . [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Vector4 left, Vector4 right) - { - return (left.X == right.X) - && (left.Y == right.Y) - && (left.Z == right.Z) - && (left.W == right.W); - } + public static bool operator ==(Vector4 left, Vector4 right) => left.AsVector128() == right.AsVector128(); /// Returns a value that indicates whether two specified vectors are not equal. /// The first vector to compare. /// The second vector to compare. /// if and are not equal; otherwise, . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector4 left, Vector4 right) - { - return !(left == right); - } + public static bool operator !=(Vector4 left, Vector4 right) => !(left == right); /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. @@ -223,15 +192,7 @@ public float this[int index] /// The method defines the multiplication operation for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator *(Vector4 left, Vector4 right) - { - return new Vector4( - left.X * right.X, - left.Y * right.Y, - left.Z * right.Z, - left.W * right.W - ); - } + public static Vector4 operator *(Vector4 left, Vector4 right) => (left.AsVector128() * right.AsVector128()).AsVector4(); /// Multiplies the specified vector by the specified scalar value. /// The vector. @@ -239,11 +200,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator *(Vector4 left, float right) - { - return left * new Vector4(right); - } + public static Vector4 operator *(Vector4 left, float right) => left * new Vector4(right); /// Multiplies the scalar value by the specified vector. /// The vector. @@ -251,11 +208,7 @@ public float this[int index] /// The scaled vector. /// The method defines the multiplication operation for objects. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator *(float left, Vector4 right) - { - return right * left; - } + public static Vector4 operator *(float left, Vector4 right) => right * left; /// Subtracts the second vector from the first. /// The first vector. @@ -264,15 +217,7 @@ public float this[int index] /// The method defines the subtraction operation for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator -(Vector4 left, Vector4 right) - { - return new Vector4( - left.X - right.X, - left.Y - right.Y, - left.Z - right.Z, - left.W - right.W - ); - } + public static Vector4 operator -(Vector4 left, Vector4 right) => (left.AsVector128() - right.AsVector128()).AsVector4(); /// Negates the specified vector. /// The vector to negate. @@ -280,36 +225,21 @@ public float this[int index] /// The method defines the unary negation operation for objects. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 operator -(Vector4 value) - { - return Zero - value; - } + public static Vector4 operator -(Vector4 value) => (-value.AsVector128()).AsVector4(); /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. /// A vector. /// The absolute value vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Abs(Vector4 value) - { - return new Vector4( - MathF.Abs(value.X), - MathF.Abs(value.Y), - MathF.Abs(value.Z), - MathF.Abs(value.W) - ); - } + public static Vector4 Abs(Vector4 value) => Vector128.Abs(value.AsVector128()).AsVector4(); /// Adds two vectors together. /// The first vector to add. /// The second vector to add. /// The summed vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Add(Vector4 left, Vector4 right) - { - return left + right; - } + public static Vector4 Add(Vector4 left, Vector4 right) => left + right; /// Restricts a vector between a minimum and a maximum value. /// The vector to restrict. @@ -317,7 +247,6 @@ public static Vector4 Add(Vector4 left, Vector4 right) /// The maximum value. /// The restricted vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max) { // We must follow HLSL behavior in the case user specified min value is bigger than max value. @@ -329,46 +258,28 @@ public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max) /// The second point. /// The distance. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Distance(Vector4 value1, Vector4 value2) - { - float distanceSquared = DistanceSquared(value1, value2); - return MathF.Sqrt(distanceSquared); - } + public static float Distance(Vector4 value1, Vector4 value2) => MathF.Sqrt(DistanceSquared(value1, value2)); /// Returns the Euclidean distance squared between two specified points. /// The first point. /// The second point. /// The distance squared. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistanceSquared(Vector4 value1, Vector4 value2) - { - Vector4 difference = value1 - value2; - return Dot(difference, difference); - } + public static float DistanceSquared(Vector4 value1, Vector4 value2) => (value1 - value2).LengthSquared(); /// Divides the first vector by the second. /// The first vector. /// The second vector. /// The vector resulting from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Divide(Vector4 left, Vector4 right) - { - return left / right; - } + public static Vector4 Divide(Vector4 left, Vector4 right) => left / right; /// Divides the specified vector by a specified scalar value. /// The vector. /// The scalar value. /// The vector that results from the division. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Divide(Vector4 left, float divisor) - { - return left / divisor; - } + public static Vector4 Divide(Vector4 left, float divisor) => left / divisor; /// Returns the dot product of two vectors. /// The first vector. @@ -376,26 +287,12 @@ public static Vector4 Divide(Vector4 left, float divisor) /// The dot product. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(Vector4 vector1, Vector4 vector2) - { - return (vector1.X * vector2.X) - + (vector1.Y * vector2.Y) - + (vector1.Z * vector2.Z) - + (vector1.W * vector2.W); - } + public static float Dot(Vector4 vector1, Vector4 vector2) => Vector128.Dot(vector1.AsVector128(), vector2.AsVector128()); /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 FusedMultiplyAdd(Vector4 left, Vector4 right, Vector4 addend) - { - return new Vector4( - float.FusedMultiplyAdd(left.X, right.X, addend.X), - float.FusedMultiplyAdd(left.Y, right.Y, addend.Y), - float.FusedMultiplyAdd(left.Z, right.Z, addend.Z), - float.FusedMultiplyAdd(left.W, right.W, addend.W) - ); - } + public static Vector4 FusedMultiplyAdd(Vector4 left, Vector4 right, Vector4 addend) => Vector128.FusedMultiplyAdd(left.AsVector128(), right.AsVector128(), addend.AsVector128()).AsVector4(); /// Performs a linear interpolation between two vectors based on the given weighting. /// The first vector. @@ -407,10 +304,7 @@ public static Vector4 FusedMultiplyAdd(Vector4 left, Vector4 right, Vector4 adde /// ]]> [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) - { - return (value1 * (1.0f - amount)) + (value2 * amount); - } + public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) => (value1 * (1.0f - amount)) + (value2 * amount); /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. /// The first vector. @@ -418,15 +312,7 @@ public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) /// The maximized vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Max(Vector4 value1, Vector4 value2) - { - return new Vector4( - (value1.X > value2.X) ? value1.X : value2.X, - (value1.Y > value2.Y) ? value1.Y : value2.Y, - (value1.Z > value2.Z) ? value1.Z : value2.Z, - (value1.W > value2.W) ? value1.W : value2.W - ); - } + public static Vector4 Max(Vector4 value1, Vector4 value2) => Vector128.Max(value1.AsVector128(), value2.AsVector128()).AsVector4(); /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors. /// The first vector. @@ -434,115 +320,65 @@ public static Vector4 Max(Vector4 value1, Vector4 value2) /// The minimized vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Min(Vector4 value1, Vector4 value2) - { - return new Vector4( - (value1.X < value2.X) ? value1.X : value2.X, - (value1.Y < value2.Y) ? value1.Y : value2.Y, - (value1.Z < value2.Z) ? value1.Z : value2.Z, - (value1.W < value2.W) ? value1.W : value2.W - ); - } + public static Vector4 Min(Vector4 value1, Vector4 value2) => Vector128.Min(value1.AsVector128(), value2.AsVector128()).AsVector4(); /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// The first vector. /// The second vector. /// The element-wise product vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Multiply(Vector4 left, Vector4 right) - { - return left * right; - } + public static Vector4 Multiply(Vector4 left, Vector4 right) => left * right; /// Multiplies a vector by a specified scalar. /// The vector to multiply. /// The scalar value. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Multiply(Vector4 left, float right) - { - return left * right; - } + public static Vector4 Multiply(Vector4 left, float right) => left * right; /// Multiplies a scalar value by a specified vector. /// The scaled value. /// The vector. /// The scaled vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Multiply(float left, Vector4 right) - { - return left * right; - } + public static Vector4 Multiply(float left, Vector4 right) => left * right; /// [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 MultiplyAddEstimate(Vector4 left, Vector4 right, Vector4 addend) - { - return new Vector4( - float.MultiplyAddEstimate(left.X, right.X, addend.X), - float.MultiplyAddEstimate(left.Y, right.Y, addend.Y), - float.MultiplyAddEstimate(left.Z, right.Z, addend.Z), - float.MultiplyAddEstimate(left.W, right.W, addend.W) - ); - } + public static Vector4 MultiplyAddEstimate(Vector4 left, Vector4 right, Vector4 addend) => Vector128.MultiplyAddEstimate(left.AsVector128(), right.AsVector128(), addend.AsVector128()).AsVector4(); /// Negates a specified vector. /// The vector to negate. /// The negated vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Negate(Vector4 value) - { - return -value; - } + public static Vector4 Negate(Vector4 value) => -value; /// Returns a vector with the same direction as the specified vector, but with a length of one. /// The vector to normalize. /// The normalized vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Normalize(Vector4 vector) - { - return vector / vector.Length(); - } + public static Vector4 Normalize(Vector4 vector) => vector / vector.Length(); /// Returns a vector whose elements are the square root of each of a specified vector's elements. /// A vector. /// The square root vector. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 SquareRoot(Vector4 value) - { - return new Vector4( - MathF.Sqrt(value.X), - MathF.Sqrt(value.Y), - MathF.Sqrt(value.Z), - MathF.Sqrt(value.W) - ); - } + public static Vector4 SquareRoot(Vector4 value) => Vector128.Sqrt(value.AsVector128()).AsVector4(); /// Subtracts the second vector from the first. /// The first vector. /// The second vector. /// The difference vector. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Subtract(Vector4 left, Vector4 right) - { - return left - right; - } + public static Vector4 Subtract(Vector4 left, Vector4 right) => left - right; /// Transforms a two-dimensional vector by a specified 4x4 matrix. /// The vector to transform. /// The transformation matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Transform(Vector2 position, Matrix4x4 matrix) - => Transform(position, in matrix.AsImpl()); + public static Vector4 Transform(Vector2 position, Matrix4x4 matrix) => Transform(position, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector4 Transform(Vector2 position, in Matrix4x4.Impl matrix) @@ -588,9 +424,7 @@ public static Vector4 Transform(Vector2 value, Quaternion rotation) /// The vector to transform. /// The transformation matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Transform(Vector3 position, Matrix4x4 matrix) - => Transform(position, in matrix.AsImpl()); + public static Vector4 Transform(Vector3 position, Matrix4x4 matrix) => Transform(position, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector4 Transform(Vector3 position, in Matrix4x4.Impl matrix) @@ -637,9 +471,7 @@ public static Vector4 Transform(Vector3 value, Quaternion rotation) /// The vector to transform. /// The transformation matrix. /// The transformed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Transform(Vector4 vector, Matrix4x4 matrix) - => Transform(vector, in matrix.AsImpl()); + public static Vector4 Transform(Vector4 vector, Matrix4x4 matrix) => Transform(vector, in matrix.AsImpl()); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector4 Transform(Vector4 vector, in Matrix4x4.Impl matrix) @@ -687,18 +519,7 @@ public static Vector4 Transform(Vector4 value, Quaternion rotation) /// is . /// The number of elements in the current instance is greater than in the array. /// is multidimensional. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void CopyTo(float[] array) - { - // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons - - if (array.Length < Count) - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - - Unsafe.WriteUnaligned(ref Unsafe.As(ref array[0]), this); - } + public readonly void CopyTo(float[] array) => this.AsVector128().CopyTo(array); /// Copies the elements of the vector to a specified array starting at a specified index position. /// The destination array. @@ -710,125 +531,52 @@ public readonly void CopyTo(float[] array) /// -or- /// is greater than or equal to the array length. /// is multidimensional. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void CopyTo(float[] array, int index) - { - // We explicitly don't check for `null` because historically this has thrown `NullReferenceException` for perf reasons - - if ((uint)index >= (uint)array.Length) - { - ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_IndexMustBeLess(); - } - - if ((array.Length - index) < Count) - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - - Unsafe.WriteUnaligned(ref Unsafe.As(ref array[index]), this); - } + public readonly void CopyTo(float[] array, int index) => this.AsVector128().CopyTo(array, index); /// Copies the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// If number of elements in source vector is greater than those available in destination span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void CopyTo(Span destination) - { - if (destination.Length < Count) - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - - Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); - } + public readonly void CopyTo(Span destination) => this.AsVector128().CopyTo(destination); /// Attempts to copy the vector to the given . The length of the destination span must be at least 4. /// The destination span which the values are copied into. /// if the source vector was successfully copied to . if is not large enough to hold the source vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool TryCopyTo(Span destination) - { - if (destination.Length < Count) - { - return false; - } - - Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); - return true; - } + public readonly bool TryCopyTo(Span destination) => this.AsVector128().TryCopyTo(destination); /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. /// if the two vectors are equal; otherwise, . /// Two vectors are equal if their , , , and elements are equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Vector4 other) - { - // This function needs to account for floating-point equality around NaN - // and so must behave equivalently to the underlying float/double.Equals - - if (Vector128.IsHardwareAccelerated) - { - return this.AsVector128().Equals(other.AsVector128()); - } - - return SoftwareFallback(in this, other); - - static bool SoftwareFallback(in Vector4 self, Vector4 other) - { - return self.X.Equals(other.X) - && self.Y.Equals(other.Y) - && self.Z.Equals(other.Z) - && self.W.Equals(other.W); - } - } + public readonly bool Equals(Vector4 other) => this.AsVector128().Equals(other.AsVector128()); /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . /// The current instance and are equal if is a object and their corresponding elements are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly bool Equals([NotNullWhen(true)] object? obj) - { - return (obj is Vector4 other) && Equals(other); - } + public override readonly bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector4 other) && Equals(other); /// Returns the hash code for this instance. /// The hash code. - public override readonly int GetHashCode() - { - return HashCode.Combine(X, Y, Z, W); - } + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z, W); /// Returns the length of this vector object. /// The vector's length. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Length() - { - float lengthSquared = LengthSquared(); - return MathF.Sqrt(lengthSquared); - } + public readonly float Length() => MathF.Sqrt(LengthSquared()); /// Returns the length of the vector squared. /// The vector's length squared. /// This operation offers better performance than a call to the method. /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float LengthSquared() - { - return Dot(this, this); - } + public readonly float LengthSquared() => Dot(this, this); /// Returns the string representation of the current instance using default formatting. /// The string representation of the current instance. /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. - public override readonly string ToString() - { - return ToString("G", CultureInfo.CurrentCulture); - } + public override readonly string ToString() => ToString("G", CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements. /// A standard or custom numeric format string that defines the format of individual elements. @@ -836,10 +584,7 @@ public override readonly string ToString() /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. /// Standard Numeric Format Strings /// Custom Numeric Format Strings - public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) - { - return ToString(format, CultureInfo.CurrentCulture); - } + public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) => ToString(format, CultureInfo.CurrentCulture); /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. /// A standard or custom numeric format string that defines the format of individual elements. diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs index 4ad327aebc9ee..4009387a4f796 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs @@ -124,7 +124,6 @@ public unsafe Vector(ReadOnlySpan values) /// The span from which the vector is created. /// A new with its elements set to the first elements from . /// The length of is less than . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector(Span values) : this((ReadOnlySpan)values) { } @@ -134,12 +133,7 @@ public Vector(Span values) : this((ReadOnlySpan)values) public static Vector AllBitsSet { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T scalar = Scalar.AllBitsSet; - return new Vector(scalar); - } + get => new Vector(Scalar.AllBitsSet); } #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -148,7 +142,6 @@ public static Vector AllBitsSet public static unsafe int Count { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -202,12 +195,7 @@ public static bool IsSupported public static Vector One { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T scalar = Scalar.One; - return new Vector(scalar); - } + get => new Vector(Scalar.One); } /// Gets a new with all elements initialized to zero. @@ -215,7 +203,6 @@ public static Vector One public static Vector Zero { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -223,13 +210,7 @@ public static Vector Zero } } - internal string DisplayString - { - get - { - return IsSupported ? ToString() : SR.NotSupported_Type; - } - } + internal string DisplayString => IsSupported ? ToString() : SR.NotSupported_Type; /// Gets the element at the specified index. /// The index of the element to get. @@ -239,11 +220,7 @@ internal string DisplayString public T this[int index] { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.GetElement(index); - } + get => this.GetElement(index); } /// Adds two vectors to compute their sum. @@ -395,7 +372,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -403,7 +379,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -411,7 +386,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -419,7 +393,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -427,7 +400,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -435,7 +407,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -444,7 +415,6 @@ public T this[int index] /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -453,7 +423,6 @@ public T this[int index] /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -461,7 +430,6 @@ public T this[int index] /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -470,7 +438,6 @@ public T this[int index] /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -479,7 +446,6 @@ public T this[int index] /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Reinterprets a as a new . @@ -488,7 +454,6 @@ public T this[int index] /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector(Vector value) => value.As(); /// Compares two vectors to determine if any elements are not equal. @@ -496,18 +461,7 @@ public T this[int index] /// The vector to compare with . /// true if any elements in was not equal to the corresponding element in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector left, Vector right) - { - for (int index = 0; index < Count; index++) - { - if (!Scalar.Equals(left.GetElementUnsafe(index), right.GetElementUnsafe(index))) - { - return true; - } - } - return false; - } + public static bool operator !=(Vector left, Vector right) => !(left == right); /// Shifts each element of a vector left by the specified amount. /// The vector whose elements are to be shifted. @@ -552,26 +506,13 @@ public T this[int index] /// The scalar to multiply with . /// The product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector operator *(Vector value, T factor) - { - Unsafe.SkipInit(out Vector result); - - for (int index = 0; index < Count; index++) - { - T element = Scalar.Multiply(value.GetElementUnsafe(index), factor); - result.SetElementUnsafe(index, element); - } - - return result; - } + public static Vector operator *(Vector value, T factor) => value * new Vector(factor); /// Multiplies a vector by a scalar to compute their product. /// The scalar to multiply with . /// The vector to multiply with . /// The product of and . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector operator *(T factor, Vector value) => value * factor; /// Computes the ones-complement of a vector. @@ -637,7 +578,6 @@ public static Vector operator >>(Vector value, int shiftCount) /// The vector to negate. /// A vector whose elements are the unary negation of the corresponding elements in . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector operator -(Vector value) => Zero - value; /// Returns a given vector unchanged. @@ -645,7 +585,6 @@ public static Vector operator >>(Vector value, int shiftCount) /// /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector operator +(Vector value) { ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); @@ -745,7 +684,6 @@ public void CopyTo(Span destination) /// Returns a boolean indicating whether the given Object is equal to this vector instance. /// The Object to compare against. /// True if the Object is equal to this vector; False otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector other) && Equals(other); /// Returns a boolean indicating whether the given vector is equal to this vector instance. diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs index e2c49c820d773..0b029d02fe320 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs @@ -56,7 +56,7 @@ public ReadOnlySpan(T[]? array) /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. - /// The index at which to begin the read-only span. + /// The zero-based index at which to begin the read-only span. /// The number of items in the read-only span. /// Returns default when is null. /// @@ -134,7 +134,7 @@ internal ReadOnlySpan(ref T reference, int length) /// /// Returns the specified element of the read-only span. /// - /// + /// The zero-based index. /// /// /// Thrown when index less than 0 or index greater than or equal to Length @@ -352,7 +352,7 @@ public override string ToString() /// /// Forms a slice out of the given read-only span, beginning at 'start'. /// - /// The index at which to begin this slice. + /// The zero-based index at which to begin this slice. /// /// Thrown when the specified index is not in range (<0 or >Length). /// @@ -368,7 +368,7 @@ public ReadOnlySpan Slice(int start) /// /// Forms a slice out of the given read-only span, beginning at 'start', of given length /// - /// The index at which to begin this slice. + /// The zero-based index at which to begin this slice. /// The desired length for the slice (exclusive). /// /// Thrown when the specified or end index is not in range (<0 or >Length). diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs index 22f9e762d473b..cc7a2faa23426 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastCache.cs @@ -91,11 +91,10 @@ private static int KeyToBucket(ref int tableData, nuint source, nuint target) // then we use fibonacci hashing to reduce the value to desired size. int hashShift = HashShift(ref tableData); + nuint hash = BitOperations.RotateLeft(source, (nuint.Size * 8) / 2) ^ target; #if TARGET_64BIT - ulong hash = BitOperations.RotateLeft((ulong)source, 32) ^ (ulong)target; return (int)((hash * 11400714819323198485ul) >> hashShift); #else - uint hash = BitOperations.RotateLeft((uint)source, 16) ^ (uint)target; return (int)((hash * 2654435769u) >> hashShift); #endif } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 3715e6df74a11..d1efa61d6cecf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -163,5 +163,9 @@ internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstE [Intrinsic] internal static bool IsKnownConstant(T t) where T : struct => false; #pragma warning restore IDE0060 + + /// true if the given type is a reference type or a value type that contains references or by-refs; otherwise, false. + [Intrinsic] + public static bool IsReferenceOrContainsReferences() where T: allows ref struct => IsReferenceOrContainsReferences(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index e59b30fd7633f..286435be8cc0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -33,6 +33,7 @@ public static unsafe partial class Unsafe [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void* AsPointer(ref T value) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -51,6 +52,7 @@ public static unsafe partial class Unsafe [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SizeOf() + where T : allows ref struct { return sizeof(T); } @@ -83,6 +85,8 @@ public static T As(object? o) where T : class? [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TTo As(ref TFrom source) + where TFrom : allows ref struct + where TTo : allows ref struct { throw new PlatformNotSupportedException(); @@ -100,6 +104,7 @@ public static ref TTo As(ref TFrom source) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, int elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -126,6 +131,7 @@ public static ref T Add(ref T source, int elementOffset) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, IntPtr elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -153,6 +159,7 @@ public static ref T Add(ref T source, IntPtr elementOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void* Add(void* source, int elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -179,6 +186,7 @@ public static ref T Add(ref T source, IntPtr elementOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, nuint elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -206,6 +214,7 @@ public static ref T Add(ref T source, nuint elementOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AddByteOffset(ref T source, nuint byteOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -230,6 +239,7 @@ public static ref T AddByteOffset(ref T source, nuint byteOffset) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool AreSame([AllowNull] ref readonly T left, [AllowNull] ref readonly T right) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -248,6 +258,8 @@ public static bool AreSame([AllowNull] ref readonly T left, [AllowNull] ref r [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TTo BitCast(TFrom source) + where TFrom : allows ref struct + where TTo : allows ref struct { if (sizeof(TFrom) != sizeof(TTo) || default(TFrom) is null || default(TTo) is null) { @@ -265,6 +277,7 @@ public static TTo BitCast(TFrom source) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Copy(void* destination, ref readonly T source) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -284,6 +297,7 @@ public static void Copy(void* destination, ref readonly T source) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Copy(ref T destination, void* source) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -386,6 +400,7 @@ public static void CopyBlockUnaligned(ref byte destination, ref readonly byte so [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAddressGreaterThan([AllowNull] ref readonly T left, [AllowNull] ref readonly T right) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -409,6 +424,7 @@ public static bool IsAddressGreaterThan([AllowNull] ref readonly T left, [All [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAddressLessThan([AllowNull] ref readonly T left, [AllowNull] ref readonly T right) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -514,6 +530,7 @@ public static void InitBlockUnaligned(ref byte startAddress, byte value, uint by [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ReadUnaligned(void* source) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -537,7 +554,8 @@ public static T ReadUnaligned(void* source) // Mono:ReadUnaligned [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ReadUnaligned(ref readonly byte source) + public static T ReadUnaligned(scoped ref readonly byte source) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -563,6 +581,7 @@ public static T ReadUnaligned(ref readonly byte source) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUnaligned(void* destination, T value) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -588,6 +607,7 @@ public static void WriteUnaligned(void* destination, T value) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUnaligned(ref byte destination, T value) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); // Type token used by the actual method body @@ -613,6 +633,7 @@ public static void WriteUnaligned(ref byte destination, T value) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AddByteOffset(ref T source, IntPtr byteOffset) + where T : allows ref struct { // This method is implemented by the toolchain throw new PlatformNotSupportedException(); @@ -631,6 +652,7 @@ public static ref T AddByteOffset(ref T source, IntPtr byteOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Read(void* source) + where T : allows ref struct { return *(T*)source; } @@ -643,6 +665,7 @@ public static T Read(void* source) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(void* destination, T value) + where T : allows ref struct { *(T*)destination = value; } @@ -655,6 +678,7 @@ public static void Write(void* destination, T value) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AsRef(void* source) + where T : allows ref struct { return ref *(T*)source; } @@ -670,6 +694,7 @@ public static ref T AsRef(void* source) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AsRef(scoped ref readonly T source) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -687,6 +712,7 @@ public static ref T AsRef(scoped ref readonly T source) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IntPtr ByteOffset([AllowNull] ref readonly T origin, [AllowNull] ref readonly T target) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -705,6 +731,7 @@ public static IntPtr ByteOffset([AllowNull] ref readonly T origin, [AllowNull [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T NullRef() + where T : allows ref struct { return ref AsRef(null); @@ -725,6 +752,7 @@ public static ref T NullRef() [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullRef(ref readonly T source) + where T : allows ref struct { return AsPointer(ref Unsafe.AsRef(in source)) == null; @@ -745,6 +773,7 @@ public static bool IsNullRef(ref readonly T source) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SkipInit(out T value) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -759,6 +788,7 @@ public static void SkipInit(out T value) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, int elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -785,6 +815,7 @@ public static ref T Subtract(ref T source, int elementOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void* Subtract(void* source, int elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -810,6 +841,7 @@ public static ref T Subtract(ref T source, int elementOffset) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, IntPtr elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -835,6 +867,7 @@ public static ref T Subtract(ref T source, IntPtr elementOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, nuint elementOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); @@ -859,6 +892,7 @@ public static ref T Subtract(ref T source, nuint elementOffset) [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T SubtractByteOffset(ref T source, IntPtr byteOffset) + where T : allows ref struct { throw new PlatformNotSupportedException(); @@ -877,6 +911,7 @@ public static ref T SubtractByteOffset(ref T source, IntPtr byteOffset) [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T SubtractByteOffset(ref T source, nuint byteOffset) + where T : allows ref struct { #if CORECLR typeof(T).ToString(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CLong.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CLong.cs index 491f4ddbab53f..520d9e92b7d22 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CLong.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CLong.cs @@ -29,7 +29,7 @@ namespace System.Runtime.InteropServices /// /// Constructs an instance from a 32-bit integer. /// - /// The integer vaule. + /// The integer value. public CLong(int value) { _value = (NativeType)value; @@ -38,7 +38,7 @@ public CLong(int value) /// /// Constructs an instance from a native sized integer. /// - /// The integer vaule. + /// The integer value. /// is outside the range of the underlying storage type. public CLong(nint value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CULong.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CULong.cs index 74bc9b35ad62a..13ee5a7e01102 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CULong.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CULong.cs @@ -29,7 +29,7 @@ namespace System.Runtime.InteropServices /// /// Constructs an instance from a 32-bit unsigned integer. /// - /// The integer vaule. + /// The integer value. public CULong(uint value) { _value = (NativeType)value; @@ -38,7 +38,7 @@ public CULong(uint value) /// /// Constructs an instance from a native sized unsigned integer. /// - /// The integer vaule. + /// The integer value. /// is outside the range of the underlying storage type. public CULong(nuint value) { 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 e3cecd684abf6..9c372574ed0de 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 @@ -55,6 +55,23 @@ public static Span AsSpan(List? list) public static ref TValue GetValueRefOrNullRef(Dictionary dictionary, TKey key) where TKey : notnull => ref dictionary.FindValue(key); + /// + /// Gets either a ref to a in the or a ref null if it does not exist in the . + /// + /// The dictionary to get the ref to from. + /// The key used for lookup. + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + /// The type of an alternate key for lookups in the dictionary. + /// + /// Items should not be added or removed from the while the ref is in use. + /// The ref null can be detected using System.Runtime.CompilerServices.Unsafe.IsNullRef + /// + public static ref TValue GetValueRefOrNullRef(Dictionary.AlternateLookup dictionary, TAlternateKey key) + where TKey : notnull + where TAlternateKey : notnull, allows ref struct + => ref dictionary.FindValue(key, out _); + /// /// Gets a ref to a in the , adding a new entry with a default value if it does not exist in the . /// @@ -67,6 +84,21 @@ public static ref TValue GetValueRefOrNullRef(Dictionary(Dictionary dictionary, TKey key, out bool exists) where TKey : notnull => ref Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault(dictionary, key, out exists); + /// + /// Gets a ref to a in the , adding a new entry with a default value if it does not exist in the . + /// + /// The dictionary to get the ref to from. + /// The key used for lookup. + /// Whether or not a new entry for the given key was added to the dictionary. + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + /// The type of the alternate key in the dictionary lookup. + /// Items should not be added to or removed from the while the ref is in use. + public static ref TValue? GetValueRefOrAddDefault(Dictionary.AlternateLookup dictionary, TAlternateKey key, out bool exists) + where TKey : notnull + where TAlternateKey : notnull, allows ref struct + => ref dictionary.GetValueRefOrAddDefault(key, out exists); + /// /// Sets the count of the to the specified value. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs index a6335019d099f..afc824b8025e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs @@ -20,13 +20,13 @@ public enum CreateComInterfaceFlags /// /// This is useful in scenarios when the caller has no need to rely on an IUnknown instance /// that is used when running managed code is not possible (i.e. during a GC). In traditional - /// COM scenarios this is common, but scenarios involving Reference Tracker hosting + /// COM scenarios this is common, but scenarios involving Reference Tracker hosting /// calling of the IUnknown API during a GC is possible. /// CallerDefinedIUnknown = 1, /// - /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. + /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown /// and as such none should be supplied by the caller. /// @@ -42,7 +42,7 @@ public enum CreateObjectFlags None = 0, /// - /// Indicate if the supplied external COM object implements the IReferenceTracker. + /// Indicate if the supplied external COM object implements the IReferenceTracker. /// TrackerObject = 1, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs index 0fe1dc698fd0a..5a77f4c846e78 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs @@ -36,10 +36,10 @@ ** operations against a single handle at the same time or block their ** access to Close and Dispose unless you are very comfortable with the ** semantics of passing an invalid (or possibly invalidated and -** reallocated) to the unamanged routines you marshal your handle to +** reallocated) to the unmanaged routines you marshal your handle to ** (and the effects of closing such a handle while those calls are in ** progress). The runtime cannot protect you from undefined program -** behvior that might result from such scenarios. You have been warned. +** behavior that might result from such scenarios. You have been warned. ** ** ===========================================================*/ diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomQueryInterfaceResult.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomQueryInterfaceResult.cs index 753f441e42b13..8e46414979052 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomQueryInterfaceResult.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomQueryInterfaceResult.cs @@ -5,7 +5,7 @@ namespace System.Runtime.InteropServices { - // The enum of the return value of IQuerable.GetInterface + // The enum of the return value of ICustomQueryInterface.GetInterface [EditorBrowsable(EditorBrowsableState.Never)] public enum CustomQueryInterfaceResult { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/LibraryImportAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/LibraryImportAttribute.cs index dc8768a6f81de..eaa95b7c26063 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/LibraryImportAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/LibraryImportAttribute.cs @@ -18,7 +18,7 @@ namespace System.Runtime.InteropServices public #else #pragma warning disable CS0436 // Type conflicts with imported type - // Some assemblies that target downlevel have InternalsVisibleTo to their test assembiles. + // Some assemblies that target downlevel have InternalsVisibleTo to their test assemblies. // As this is only used in this repo and isn't a problem in shipping code, // just disable the duplicate type warning. internal diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 25bc1f195fa44..b22cc78e6edc5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -550,7 +550,7 @@ public static void StructureToPtr([DisallowNull] T structure, IntPtr ptr, boo } /// - /// Creates a new instance of "structuretype" and marshals data from a + /// Creates a new instance of and marshals data from a /// native memory block to it. /// [RequiresDynamicCode("Marshalling code for the object might not be available")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index 344f528a45c5a..f0e1f6d28aeac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -780,19 +780,7 @@ public int CompareTo(object? obj) { if (obj is NFloat other) { - if (_value < other._value) return -1; - if (_value > other._value) return 1; - if (_value == other._value) return 0; - - // At least one of the values is NaN. - if (NativeType.IsNaN(_value)) - { - return NativeType.IsNaN(other._value) ? 0 : -1; - } - else - { - return 1; - } + return CompareTo(other); } else if (obj is null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index 9a81662c0305f..41321edd86325 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -225,7 +225,7 @@ public static void SetDllImportResolver(Assembly assembly, DllImportResolver res /// The native library to load. /// The assembly trying load the native library. /// If the pInvoke has DefaultDllImportSearchPathAttribute. - /// If hasdllImportSearchPathFlags is true, the flags in + /// If is true, the flags in /// DefaultDllImportSearchPathAttribute; meaningless otherwise /// The handle for the loaded library on success. Null on failure. internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/RuntimeInformation.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/RuntimeInformation.cs index e750d1011584c..9387967c49a4c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/RuntimeInformation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/RuntimeInformation.cs @@ -15,7 +15,7 @@ public static partial class RuntimeInformation /// and processor architecture of the currently executing process. /// Since this string is opaque, it is not recommended to parse the string into its constituent parts. /// - /// For more information, see https://docs.microsoft.com/dotnet/core/rid-catalog. + /// For more information, see https://learn.microsoft.com/dotnet/core/rid-catalog. /// public static string RuntimeIdentifier => s_runtimeIdentifier ??= AppContext.GetData("RUNTIME_IDENTIFIER") as string ?? "unknown"; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StringMarshalling.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StringMarshalling.cs index 503b4e4cf149d..b31d024cea8e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StringMarshalling.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StringMarshalling.cs @@ -21,7 +21,7 @@ namespace System.Runtime.InteropServices enum StringMarshalling { /// - /// Indicates the user is suppling a specific marshaller in . + /// Indicates the user is supplying a specific marshaller in . /// Custom = 0, /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SuppressGCTransitionAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SuppressGCTransitionAttribute.cs index b41d10ee520e9..d589dfb32bdc5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SuppressGCTransitionAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SuppressGCTransitionAttribute.cs @@ -36,7 +36,7 @@ namespace System.Runtime.InteropServices /// Suppressing the GC transition is an advanced scenario and should not be done without fully understanding /// potential consequences. /// - /// One of these consequences is an impact to Mixed-mode debugging (https://docs.microsoft.com/visualstudio/debugger/how-to-debug-in-mixed-mode). + /// One of these consequences is an impact to Mixed-mode debugging (https://learn.microsoft.com/visualstudio/debugger/how-to-debug-in-mixed-mode). /// During Mixed-mode debugging, it is not possible to step into or set breakpoints in a P/Invoke that /// has been marked with this attribute. A workaround is to switch to native debugging and set a breakpoint in the native function. /// In general, usage of this attribute is not recommended if debugging the P/Invoke is important, for example diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs index 411260e321d79..114ae92bb958d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs @@ -5,14 +5,14 @@ namespace System.Runtime.InteropServices { /// /// Any method marked with can be directly called from - /// native code. The function token can be loaded to a local variable using the address-of operator + /// native code. The function token can be loaded to a local variable using the address-of operator /// in C# and passed as a callback to a native method. /// /// /// Methods marked with this attribute have the following restrictions: /// * Method must be marked "static". /// * Must not be called from managed code. - /// * Must only have blittable arguments. + /// * Must only have blittable arguments. /// [AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class UnmanagedCallersOnlyAttribute : Attribute diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index ece3b5c88588a..bbce99bcf0371 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -640,7 +640,153 @@ internal Arm64() { } /// public static unsafe Vector BooleanNot(Vector value) { throw new PlatformNotSupportedException(); } + /// Compute vector addresses for 16-bit data + /// + /// svuint32_t svadrh[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svadrh[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrh[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrh[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + + /// Compute vector addresses for 32-bit data + + /// + /// svuint32_t svadrw[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svadrw[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrw[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrw[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + + /// Compute vector addresses for 64-bit data + + /// + /// svuint32_t svadrd[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svadrd[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrd[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrd[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + + /// Compute vector addresses for 8-bit data + + /// + /// svuint32_t svadrb[_u32base]_[s32]offset(svuint32_t bases, svint32_t offsets) + /// ADR Zresult.S, [Zbases.S, Zoffsets.S] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svadrb[_u32base]_[u32]offset(svuint32_t bases, svuint32_t offsets) + /// ADR Zresult.S, [Zbases.S, Zoffsets.S] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrb[_u64base]_[s64]offset(svuint64_t bases, svint64_t offsets) + /// ADR Zresult.D, [Zbases.D, Zoffsets.D] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svadrb[_u64base]_[u64]offset(svuint64_t bases, svuint64_t offsets) + /// ADR Zresult.D, [Zbases.D, Zoffsets.D] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + + /// Shuffle active elements of vector to the right and fill with zero + + /// + /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + + /// Compare equal to /// ConditionalSelect : Conditionally select elements /// @@ -1320,6 +1466,69 @@ internal Arm64() { } public static unsafe Vector FusedMultiplySubtractNegated(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// Count set predicate bits + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b16(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.H + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b32(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.S + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svcntp_b64(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.D + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) { throw new PlatformNotSupportedException(); } + + /// Count leading sign bits /// @@ -1710,970 +1919,2038 @@ internal Arm64() { } /// public static unsafe Vector LoadVectorUInt32ZeroExtendToUInt64(Vector mask, uint* address) { throw new PlatformNotSupportedException(); } - /// Max : Maximum + /// + /// svint16_t svldnf1ub_s16(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt16(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svmax[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmax[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmax[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// UMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// UMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svint32_t svldnf1ub_s32(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt32(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmax[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmax[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmax[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint64_t svldnf1ub_s64(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt64(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svmax[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmax[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmax[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// SMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// SMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint16_t svldnf1ub_u16(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.H, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt16(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svmax[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svmax[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svmax[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// SMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// SMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint32_t svldnf1ub_u32(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt32(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svmax[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmax[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmax[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// SMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// SMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint64_t svldnf1ub_u64(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt64(byte* address) { throw new PlatformNotSupportedException(); } /// - /// svint8_t svmax[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmax[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmax[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// SMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// SMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svint32_t svldnf1uh_s32(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToInt32(ushort* address) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmax[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmax[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmax[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svint64_t svldnf1uh_s64(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToInt64(ushort* address) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svmax[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmax[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmax[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// UMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// UMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint32_t svldnf1uh_u32(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToUInt32(ushort* address) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svmax[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmax[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmax[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// UMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// UMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint64_t svldnf1uh_u64(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToUInt64(ushort* address) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svmax[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmax[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmax[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// UMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// UMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint64_t svldnf1uw_s64(svbool_t pg, const uint32_t *base) + /// LDNF1W Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector LoadVectorUInt32NonFaultingZeroExtendToInt64(uint* address) { throw new PlatformNotSupportedException(); } + /// + /// svuint64_t svldnf1uw_u64(svbool_t pg, const uint32_t *base) + /// LDNF1W Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorUInt32NonFaultingZeroExtendToUInt64(uint* address) { throw new PlatformNotSupportedException(); } - /// MaxAcross : Maximum reduction to scalar + /// + /// svuint8x2_t svld2[_u8](svbool_t pg, const uint8_t *base) + /// LD2B {Zresult0.B, Zresult1.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, byte* address) { throw new PlatformNotSupportedException(); } /// - /// uint8_t svmaxv[_u8](svbool_t pg, svuint8_t op) - /// UMAXV Bresult, Pg, Zop.B + /// svfloat64x2_t svld2[_f64](svbool_t pg, const float64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, double* address) { throw new PlatformNotSupportedException(); } /// - /// float64_t svmaxv[_f64](svbool_t pg, svfloat64_t op) - /// FMAXV Dresult, Pg, Zop.D + /// svint16x2_t svld2[_s16](svbool_t pg, const int16_t *base) + /// LD2H {Zresult0.H, Zresult1.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, short* address) { throw new PlatformNotSupportedException(); } /// - /// int16_t svmaxv[_s16](svbool_t pg, svint16_t op) - /// SMAXV Hresult, Pg, Zop.H + /// svint32x2_t svld2[_s32](svbool_t pg, const int32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, int* address) { throw new PlatformNotSupportedException(); } /// - /// int32_t svmaxv[_s32](svbool_t pg, svint32_t op) - /// SMAXV Sresult, Pg, Zop.S + /// svint64x2_t svld2[_s64](svbool_t pg, const int64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, long* address) { throw new PlatformNotSupportedException(); } /// - /// int64_t svmaxv[_s64](svbool_t pg, svint64_t op) - /// SMAXV Dresult, Pg, Zop.D + /// svint8x2_t svld2[_s8](svbool_t pg, const int8_t *base) + /// LD2B {Zresult0.B, Zresult1.B}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, sbyte* address) { throw new PlatformNotSupportedException(); } /// - /// int8_t svmaxv[_s8](svbool_t pg, svint8_t op) - /// SMAXV Bresult, Pg, Zop.B + /// svfloat32x2_t svld2[_f32](svbool_t pg, const float32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, float* address) { throw new PlatformNotSupportedException(); } /// - /// float32_t svmaxv[_f32](svbool_t pg, svfloat32_t op) - /// FMAXV Sresult, Pg, Zop.S + /// svuint16x2_t svld2[_u16](svbool_t pg, const uint16_t *base) + /// LD2H {Zresult0.H, Zresult1.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, ushort* address) { throw new PlatformNotSupportedException(); } /// - /// uint16_t svmaxv[_u16](svbool_t pg, svuint16_t op) - /// UMAXV Hresult, Pg, Zop.H + /// svuint32x2_t svld2[_u32](svbool_t pg, const uint32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, uint* address) { throw new PlatformNotSupportedException(); } /// - /// uint32_t svmaxv[_u32](svbool_t pg, svuint32_t op) - /// UMAXV Sresult, Pg, Zop.S + /// svuint64x2_t svld2[_u64](svbool_t pg, const uint64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, ulong* address) { throw new PlatformNotSupportedException(); } /// - /// uint64_t svmaxv[_u64](svbool_t pg, svuint64_t op) - /// UMAXV Dresult, Pg, Zop.D + /// svuint8x3_t svld3[_u8](svbool_t pg, const uint8_t *base) + /// LD3B {Zresult0.B - Zresult2.B}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, byte* address) { throw new PlatformNotSupportedException(); } + /// + /// svfloat64x3_t svld3[_f64](svbool_t pg, const float64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, double* address) { throw new PlatformNotSupportedException(); } - /// MaxNumber : Maximum number + /// + /// svint16x3_t svld3[_s16](svbool_t pg, const int16_t *base) + /// LD3H {Zresult0.H - Zresult2.H}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, short* address) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmaxnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmaxnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmaxnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMAXNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMAXNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint32x3_t svld3[_s32](svbool_t pg, const int32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, int* address) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmaxnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmaxnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmaxnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMAXNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMAXNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svint64x3_t svld3[_s64](svbool_t pg, const int64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, long* address) { throw new PlatformNotSupportedException(); } + /// + /// svint8x3_t svld3[_s8](svbool_t pg, const int8_t *base) + /// LD3B {Zresult0.B - Zresult2.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, sbyte* address) { throw new PlatformNotSupportedException(); } - /// MaxNumberAcross : Maximum number reduction to scalar + /// + /// svfloat32x3_t svld3[_f32](svbool_t pg, const float32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, float* address) { throw new PlatformNotSupportedException(); } /// - /// float64_t svmaxnmv[_f64](svbool_t pg, svfloat64_t op) - /// FMAXNMV Dresult, Pg, Zop.D + /// svuint16x3_t svld3[_u16](svbool_t pg, const uint16_t *base) + /// LD3H {Zresult0.H - Zresult2.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, ushort* address) { throw new PlatformNotSupportedException(); } /// - /// float32_t svmaxnmv[_f32](svbool_t pg, svfloat32_t op) - /// FMAXNMV Sresult, Pg, Zop.S + /// svuint32x3_t svld3[_u32](svbool_t pg, const uint32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, uint* address) { throw new PlatformNotSupportedException(); } + /// + /// svuint64x3_t svld3[_u64](svbool_t pg, const uint64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, ulong* address) { throw new PlatformNotSupportedException(); } - /// Min : Minimum + /// + /// svuint8x4_t svld4[_u8](svbool_t pg, const uint8_t *base) + /// LD4B {Zresult0.B - Zresult3.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, byte* address) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svmin[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmin[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmin[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// UMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// UMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svfloat64x4_t svld4[_f64](svbool_t pg, const float64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, double* address) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmin[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmin[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmin[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint16x4_t svld4[_s16](svbool_t pg, const int16_t *base) + /// LD4H {Zresult0.H - Zresult3.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, short* address) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svmin[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmin[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmin[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// SMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// SMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svint32x4_t svld4[_s32](svbool_t pg, const int32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, int* address) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svmin[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint64x4_t svld4[_s64](svbool_t pg, const int64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, long* address) { throw new PlatformNotSupportedException(); } + + /// + /// svint8x4_t svld4[_s8](svbool_t pg, const int8_t *base) + /// LD4B {Zresult0.B - Zresult3.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, sbyte* address) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32x4_t svld4[_f32](svbool_t pg, const float32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, float* address) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16x4_t svld4[_u16](svbool_t pg, const uint16_t *base) + /// LD4H {Zresult0.H - Zresult3.H}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, ushort* address) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32x4_t svld4[_u32](svbool_t pg, const uint32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, uint* address) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64x4_t svld4[_u64](svbool_t pg, const uint64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, ulong* address) { throw new PlatformNotSupportedException(); } + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svint32_t svldnf1sh_s32(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToInt32(short* address) { throw new PlatformNotSupportedException(); } + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sh_s64(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToInt64(short* address) { throw new PlatformNotSupportedException(); } + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svuint32_t svldnf1sh_u32(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToUInt32(short* address) { throw new PlatformNotSupportedException(); } + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sh_u64(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToUInt64(short* address) { throw new PlatformNotSupportedException(); } + + /// Load 32-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sw_s64(svbool_t pg, const int32_t *base) + /// LDNF1SW Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt32NonFaultingSignExtendToInt64(int* address) { throw new PlatformNotSupportedException(); } + + /// Load 32-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sw_u64(svbool_t pg, const int32_t *base) + /// LDNF1SW Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt32NonFaultingSignExtendToUInt64(int* address) { throw new PlatformNotSupportedException(); } + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint16_t svldnf1sb_s16(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt16(sbyte* address) { throw new PlatformNotSupportedException(); } + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint32_t svldnf1sb_s32(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt32(sbyte* address) { throw new PlatformNotSupportedException(); } + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sb_s64(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt64(sbyte* address) { throw new PlatformNotSupportedException(); } + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint16_t svldnf1sb_u16(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt16(sbyte* address) { throw new PlatformNotSupportedException(); } + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint32_t svldnf1sb_u32(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt32(sbyte* address) { throw new PlatformNotSupportedException(); } + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sb_u64(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt64(sbyte* address) { throw new PlatformNotSupportedException(); } + + /// Max : Maximum + + /// + /// svuint8_t svmax[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmax[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmax[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// UMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// UMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svmax[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmax[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmax[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svmax[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmax[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmax[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// SMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// SMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svmax[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svmax[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svmax[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// SMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// SMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svmax[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmax[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmax[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// SMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// SMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svmax[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmax[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmax[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// SMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// SMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmax[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmax[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmax[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svmax[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmax[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmax[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// UMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// UMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svmax[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmax[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmax[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// UMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// UMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svmax[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmax[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmax[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// UMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// UMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// MaxAcross : Maximum reduction to scalar + + /// + /// uint8_t svmaxv[_u8](svbool_t pg, svuint8_t op) + /// UMAXV Bresult, Pg, Zop.B + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float64_t svmaxv[_f64](svbool_t pg, svfloat64_t op) + /// FMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int16_t svmaxv[_s16](svbool_t pg, svint16_t op) + /// SMAXV Hresult, Pg, Zop.H + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int32_t svmaxv[_s32](svbool_t pg, svint32_t op) + /// SMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svmaxv[_s64](svbool_t pg, svint64_t op) + /// SMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int8_t svmaxv[_s8](svbool_t pg, svint8_t op) + /// SMAXV Bresult, Pg, Zop.B + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svmaxv[_f32](svbool_t pg, svfloat32_t op) + /// FMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint16_t svmaxv[_u16](svbool_t pg, svuint16_t op) + /// UMAXV Hresult, Pg, Zop.H + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svmaxv[_u32](svbool_t pg, svuint32_t op) + /// UMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svmaxv[_u64](svbool_t pg, svuint64_t op) + /// UMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) { throw new PlatformNotSupportedException(); } + + + /// MaxNumber : Maximum number + + /// + /// svfloat64_t svmaxnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmaxnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmaxnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMAXNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMAXNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector MaxNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmaxnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmaxnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmaxnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMAXNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMAXNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector MaxNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// MaxNumberAcross : Maximum number reduction to scalar + + /// + /// float64_t svmaxnmv[_f64](svbool_t pg, svfloat64_t op) + /// FMAXNMV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svmaxnmv[_f32](svbool_t pg, svfloat32_t op) + /// FMAXNMV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Min : Minimum + + /// + /// svuint8_t svmin[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmin[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmin[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// UMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// UMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svmin[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmin[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmin[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svmin[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmin[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmin[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// SMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// SMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svmin[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) /// svint32_t svmin[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) /// svint32_t svmin[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) /// SMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S /// SMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svmin[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmin[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmin[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// SMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// SMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svmin[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmin[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmin[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// SMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// SMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmin[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmin[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmin[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svmin[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmin[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmin[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// UMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// UMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svmin[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmin[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmin[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// UMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// UMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svmin[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmin[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmin[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// UMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// UMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// MinAcross : Minimum reduction to scalar + + /// + /// uint8_t svminv[_u8](svbool_t pg, svuint8_t op) + /// UMINV Bresult, Pg, Zop.B + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float64_t svminv[_f64](svbool_t pg, svfloat64_t op) + /// FMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int16_t svminv[_s16](svbool_t pg, svint16_t op) + /// SMINV Hresult, Pg, Zop.H + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int32_t svminv[_s32](svbool_t pg, svint32_t op) + /// SMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svminv[_s64](svbool_t pg, svint64_t op) + /// SMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int8_t svminv[_s8](svbool_t pg, svint8_t op) + /// SMINV Bresult, Pg, Zop.B + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svminv[_f32](svbool_t pg, svfloat32_t op) + /// FMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint16_t svminv[_u16](svbool_t pg, svuint16_t op) + /// UMINV Hresult, Pg, Zop.H + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svminv[_u32](svbool_t pg, svuint32_t op) + /// UMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svminv[_u64](svbool_t pg, svuint64_t op) + /// UMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + + + /// MinNumber : Minimum number + + /// + /// svfloat64_t svminnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svminnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svminnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMINNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMINNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector MinNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svminnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svminnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svminnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMINNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMINNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector MinNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// MinNumberAcross : Minimum number reduction to scalar + + /// + /// float64_t svminnmv[_f64](svbool_t pg, svfloat64_t op) + /// FMINNMV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svminnmv[_f32](svbool_t pg, svfloat32_t op) + /// FMINNMV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// Multiply : Multiply + + /// + /// svint8_t svmul[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svint8_t svmul[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svint8_t svmul[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svmul[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svint16_t svmul[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svint16_t svmul[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svmul[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svint32_t svmul[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svint32_t svmul[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svmul[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svint64_t svmul[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svint64_t svmul[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint8_t svmul[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svuint8_t svmul[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svuint8_t svmul[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svmul[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svuint16_t svmul[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svuint16_t svmul[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svmul[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svuint32_t svmul[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svuint32_t svmul[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svmul[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svuint64_t svmul[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svuint64_t svmul[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmul[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svfloat32_t svmul[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// FMUL Zresult.S, Zop1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svfloat32_t svmul[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svmul[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svfloat64_t svmul[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// FMUL Zresult.D, Zop1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svfloat64_t svmul[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// MultiplyAdd : Multiply-add, addend first + + /// + /// svuint8_t svmla[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmla[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmla[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svmla[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmla[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmla[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svmla[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmla[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmla[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svmla[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmla[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmla[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svmla[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmla[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmla[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svmla[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmla[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmla[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svmla[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmla[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmla[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svmla[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmla[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmla[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// MultiplyBySelectedScalar : Multiply + + /// + /// svfloat64_t svmul_lane[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm_index) + /// FMUL Zresult.D, Zop1.D, Zop2.D[imm_index] + /// + public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmul_lane[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm_index) + /// FMUL Zresult.S, Zop1.S, Zop2.S[imm_index] + /// + public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) { throw new PlatformNotSupportedException(); } + + /// MultiplyExtended : Multiply extended (∞×0=2) + + /// + /// svfloat64_t svmulx[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmulx[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmulx[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMULX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// + public static unsafe Vector MultiplyExtended(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svmulx[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmulx[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmulx[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMULX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// + public static unsafe Vector MultiplyExtended(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// MultiplySubtract : Multiply-subtract, minuend first + + /// + /// svuint8_t svmls[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmls[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmls[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svmls[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmls[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmls[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svmls[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmls[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmls[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svmls[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmls[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmls[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svmls[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmls[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmls[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svmls[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmls[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmls[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svmls[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmls[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmls[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svmls[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmls[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmls[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Negate : Negate + + /// + /// svfloat64_t svneg[_f64]_m(svfloat64_t inactive, svbool_t pg, svfloat64_t op) + /// svfloat64_t svneg[_f64]_x(svbool_t pg, svfloat64_t op) + /// svfloat64_t svneg[_f64]_z(svbool_t pg, svfloat64_t op) + /// FNEG Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svneg[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) + /// svint16_t svneg[_s16]_x(svbool_t pg, svint16_t op) + /// svint16_t svneg[_s16]_z(svbool_t pg, svint16_t op) + /// NEG Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svneg[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// svint32_t svneg[_s32]_x(svbool_t pg, svint32_t op) + /// svint32_t svneg[_s32]_z(svbool_t pg, svint32_t op) + /// NEG Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svneg[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// svint64_t svneg[_s64]_x(svbool_t pg, svint64_t op) + /// svint64_t svneg[_s64]_z(svbool_t pg, svint64_t op) + /// NEG Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svneg[_s8]_x(svbool_t pg, svint8_t op) + /// svint8_t svneg[_s8]_m(svint8_t inactive, svbool_t pg, svint8_t op) + /// svint8_t svneg[_s8]_z(svbool_t pg, svint8_t op) + /// NEG Ztied.B, Pg/M, Zop.B + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svneg[_f32]_m(svfloat32_t inactive, svbool_t pg, svfloat32_t op) + /// svfloat32_t svneg[_f32]_x(svbool_t pg, svfloat32_t op) + /// svfloat32_t svneg[_f32]_z(svbool_t pg, svfloat32_t op) + /// FNEG Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + + /// Or : Bitwise inclusive OR + + /// + /// svuint8_t svorr[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svorr[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svorr[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svorr[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svorr[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svorr[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svorr[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svorr[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svorr[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svorr[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svorr[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svorr[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svorr[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svorr[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svorr[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svorr[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svorr[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svorr[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svorr[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svorr[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svorr[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svorr[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svorr[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svorr[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// OrAcross : Bitwise inclusive OR reduction to scalar + + /// + /// uint8_t svorv[_u8](svbool_t pg, svuint8_t op) + /// ORV Bresult, Pg, Zop.B + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int16_t svorv[_s16](svbool_t pg, svint16_t op) + /// ORV Hresult, Pg, Zop.H + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int32_t svorv[_s32](svbool_t pg, svint32_t op) + /// ORV Sresult, Pg, Zop.S + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svorv[_s64](svbool_t pg, svint64_t op) + /// ORV Dresult, Pg, Zop.D + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// int8_t svorv[_s8](svbool_t pg, svint8_t op) + /// ORV Bresult, Pg, Zop.B + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint16_t svorv[_u16](svbool_t pg, svuint16_t op) + /// ORV Hresult, Pg, Zop.H + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svorv[_u32](svbool_t pg, svuint32_t op) + /// ORV Sresult, Pg, Zop.S + /// + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svmin[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmin[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmin[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// SMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// SMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// uint64_t svorv[_u64](svbool_t pg, svuint64_t op) + /// ORV Dresult, Pg, Zop.D /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + + /// Count nonzero bits /// - /// svint8_t svmin[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmin[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmin[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// SMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// SMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svuint8_t svcnt[_s8]_m(svuint8_t inactive, svbool_t pg, svint8_t op) + /// svuint8_t svcnt[_s8]_x(svbool_t pg, svint8_t op) + /// svuint8_t svcnt[_s8]_z(svbool_t pg, svint8_t op) + /// CNT Ztied.B, Pg/M, Zop.B /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmin[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmin[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmin[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint8_t svcnt[_u8]_m(svuint8_t inactive, svbool_t pg, svuint8_t op) + /// svuint8_t svcnt[_u8]_x(svbool_t pg, svuint8_t op) + /// svuint8_t svcnt[_u8]_z(svbool_t pg, svuint8_t op) + /// CNT Ztied.B, Pg/M, Zop.B /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svmin[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmin[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmin[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// UMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// UMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint16_t svcnt[_s16]_m(svuint16_t inactive, svbool_t pg, svint16_t op) + /// svuint16_t svcnt[_s16]_x(svbool_t pg, svint16_t op) + /// svuint16_t svcnt[_s16]_z(svbool_t pg, svint16_t op) + /// CNT Ztied.H, Pg/M, Zop.H /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svmin[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmin[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmin[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// UMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// UMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint16_t svcnt[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) + /// svuint16_t svcnt[_u16]_x(svbool_t pg, svuint16_t op) + /// svuint16_t svcnt[_u16]_z(svbool_t pg, svuint16_t op) + /// CNT Ztied.H, Pg/M, Zop.H /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svmin[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmin[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmin[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// UMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// UMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint32_t svcnt[_s32]_m(svuint32_t inactive, svbool_t pg, svint32_t op) + /// svuint32_t svcnt[_s32]_x(svbool_t pg, svint32_t op) + /// svuint32_t svcnt[_s32]_z(svbool_t pg, svint32_t op) + /// CNT Ztied.S, Pg/M, Zop.S /// - public static unsafe Vector Min(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + /// + /// svuint32_t svcnt[_f32]_m(svuint32_t inactive, svbool_t pg, svfloat32_t op) + /// svuint32_t svcnt[_f32]_x(svbool_t pg, svfloat32_t op) + /// svuint32_t svcnt[_f32]_z(svbool_t pg, svfloat32_t op) + /// CNT Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } - /// MinAcross : Minimum reduction to scalar + /// + /// svuint32_t svcnt[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// svuint32_t svcnt[_u32]_x(svbool_t pg, svuint32_t op) + /// svuint32_t svcnt[_u32]_z(svbool_t pg, svuint32_t op) + /// CNT Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// uint8_t svminv[_u8](svbool_t pg, svuint8_t op) - /// UMINV Bresult, Pg, Zop.B + /// svuint64_t svcnt[_f64]_m(svuint64_t inactive, svbool_t pg, svfloat64_t op) + /// svuint64_t svcnt[_f64]_x(svbool_t pg, svfloat64_t op) + /// svuint64_t svcnt[_f64]_z(svbool_t pg, svfloat64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// float64_t svminv[_f64](svbool_t pg, svfloat64_t op) - /// FMINV Dresult, Pg, Zop.D + /// svuint64_t svcnt[_s64]_m(svuint64_t inactive, svbool_t pg, svint64_t op) + /// svuint64_t svcnt[_s64]_x(svbool_t pg, svint64_t op) + /// svuint64_t svcnt[_s64]_z(svbool_t pg, svint64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } /// - /// int16_t svminv[_s16](svbool_t pg, svint16_t op) - /// SMINV Hresult, Pg, Zop.H + /// svuint64_t svcnt[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// svuint64_t svcnt[_u64]_x(svbool_t pg, svuint64_t op) + /// svuint64_t svcnt[_u64]_z(svbool_t pg, svuint64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Reverse all elements /// - /// int32_t svminv[_s32](svbool_t pg, svint32_t op) - /// SMINV Sresult, Pg, Zop.S + /// svuint8_t svrev[_u8](svuint8_t op) + /// REV Zresult.B, Zop.B /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// int64_t svminv[_s64](svbool_t pg, svint64_t op) - /// SMINV Dresult, Pg, Zop.D + /// svfloat64_t svrev[_f64](svfloat64_t op) + /// REV Zresult.D, Zop.D /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// int8_t svminv[_s8](svbool_t pg, svint8_t op) - /// SMINV Bresult, Pg, Zop.B + /// svint16_t svrev[_s16](svint16_t op) + /// REV Zresult.H, Zop.H /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// float32_t svminv[_f32](svbool_t pg, svfloat32_t op) - /// FMINV Sresult, Pg, Zop.S + /// svint32_t svrev[_s32](svint32_t op) + /// REV Zresult.S, Zop.S /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// uint16_t svminv[_u16](svbool_t pg, svuint16_t op) - /// UMINV Hresult, Pg, Zop.H + /// svint64_t svrev[_s64](svint64_t op) + /// REV Zresult.D, Zop.D /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// uint32_t svminv[_u32](svbool_t pg, svuint32_t op) - /// UMINV Sresult, Pg, Zop.S + /// svint8_t svrev[_s8](svint8_t op) + /// REV Zresult.B, Zop.B /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// uint64_t svminv[_u64](svbool_t pg, svuint64_t op) - /// UMINV Dresult, Pg, Zop.D + /// svfloat32_t svrev[_f32](svfloat32_t op) + /// REV Zresult.S, Zop.S /// - public static unsafe Vector MinAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } + /// + /// svuint16_t svrev[_u16](svuint16_t op) + /// REV Zresult.H, Zop.H + /// + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } - /// MinNumber : Minimum number + /// + /// svuint32_t svrev[_u32](svuint32_t op) + /// REV Zresult.S, Zop.S + /// + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svminnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svminnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svminnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMINNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMINNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint64_t svrev[_u64](svuint64_t op) + /// REV Zresult.D, Zop.D /// - public static unsafe Vector MinNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Reverse halfwords within elements /// - /// svfloat32_t svminnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svminnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svminnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMINNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMINNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svint32_t svrevh[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// REVH Ztied.S, Pg/M, Zop.S /// - public static unsafe Vector MinNumber(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector ReverseElement16(Vector value) { throw new PlatformNotSupportedException(); } + /// + /// svint64_t svrevh[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVH Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement16(Vector value) { throw new PlatformNotSupportedException(); } - /// MinNumberAcross : Minimum number reduction to scalar + /// + /// svuint32_t svrevh[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// REVH Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement16(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svrevh[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVH Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement16(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Reverse words within elements + + /// + /// svint64_t svrevw[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVW Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement32(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svrevw[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVW Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement32(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Reverse bytes within elements + + /// + /// svint16_t svrevb[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) + /// REVB Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svrevb[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// REVB Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svrevb[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVB Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svrevb[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) + /// REVB Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svrevb[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// REVB Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svrevb[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVB Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement8(Vector value) { throw new PlatformNotSupportedException(); } + + + /// Saturating decrement by number of halfword elements + + /// + /// int32_t svqdech_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svqdech_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svqdech_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svqdech_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svqdech_pat[_s16](svint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Ztied.H, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQDECH Zresult.H, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svqdech_pat[_u16](svuint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Ztied.H, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQDECH Zresult.H, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating decrement by number of word elements + + /// + /// int32_t svqdecw_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svqdecw_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svqdecw_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svqdecw_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svqdecw_pat[_s32](svint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Ztied.S, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQDECW Zresult.S, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svqdecw_pat[_u32](svuint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Ztied.S, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQDECW Zresult.S, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating decrement by number of doubleword elements + + /// + /// int32_t svqdecd_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svqdecd_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svqdecd_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svqdecd_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svqdecd_pat[_s64](svint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Ztied.D, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQDECD Zresult.D, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svqdecd_pat[_u64](svuint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Ztied.D, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQDECD Zresult.D, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating decrement by number of byte elements + + /// + /// int32_t svqdecb_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECB Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svqdecb_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECB Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// float64_t svminnmv[_f64](svbool_t pg, svfloat64_t op) - /// FMINNMV Dresult, Pg, Zop.D + /// uint32_t svqdecb_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECB Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MinNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe uint SaturatingDecrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// float32_t svminnmv[_f32](svbool_t pg, svfloat32_t op) - /// FMINNMV Sresult, Pg, Zop.S + /// uint64_t svqdecb_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MinNumberAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } - /// Multiply : Multiply + + /// Saturating decrement by active element count /// - /// svint8_t svmul[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svint8_t svmul[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svint8_t svmul[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// int32_t svqdecp[_n_s32]_b8(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.B, Wtied /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svmul[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svint16_t svmul[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svint16_t svmul[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// int64_t svqdecp[_n_s64]_b8(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.B /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svmul[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svint32_t svmul[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svint32_t svmul[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// uint32_t svqdecp[_n_u32]_b8(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.B /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svmul[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svint64_t svmul[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svint64_t svmul[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// uint64_t svqdecp[_n_u64]_b8(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.B /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svmul[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svuint8_t svmul[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svuint8_t svmul[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// svint16_t svqdecp[_s16](svint16_t op, svbool_t pg) + /// SQDECP Ztied.H, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svmul[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svuint16_t svmul[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svuint16_t svmul[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// svint32_t svqdecp[_s32](svint32_t op, svbool_t pg) + /// SQDECP Ztied.S, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svmul[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svuint32_t svmul[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svuint32_t svmul[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// svint64_t svqdecp[_s64](svint64_t op, svbool_t pg) + /// SQDECP Ztied.D, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svmul[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svuint64_t svmul[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svuint64_t svmul[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// int32_t svqdecp[_n_s32]_b16(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.H, Wtied /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmul[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svfloat32_t svmul[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// FMUL Zresult.S, Zop1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svfloat32_t svmul[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// int64_t svqdecp[_n_s64]_b16(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.H /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmul[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svfloat64_t svmul[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// FMUL Zresult.D, Zop1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svfloat64_t svmul[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// uint32_t svqdecp[_n_u32]_b16(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.H /// - public static unsafe Vector Multiply(Vector left, Vector right) { throw new PlatformNotSupportedException(); } - - /// MultiplyAdd : Multiply-add, addend first + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svmla[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmla[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmla[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// uint64_t svqdecp[_n_u64]_b16(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.H /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svmla[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmla[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmla[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// svuint16_t svqdecp[_u16](svuint16_t op, svbool_t pg) + /// UQDECP Ztied.H, Pg /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svmla[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmla[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmla[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// int32_t svqdecp[_n_s32]_b32(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.S, Wtied /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svmla[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmla[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmla[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// int64_t svqdecp[_n_s64]_b32(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.S /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svint8_t svmla[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmla[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmla[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// uint32_t svqdecp[_n_u32]_b32(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.S /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svmla[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmla[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmla[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// uint64_t svqdecp[_n_u64]_b32(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.S /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svmla[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmla[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmla[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// svuint32_t svqdecp[_u32](svuint32_t op, svbool_t pg) + /// UQDECP Ztied.S, Pg /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svmla[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmla[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmla[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// int32_t svqdecp[_n_s32]_b64(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.D, Wtied /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } - - /// MultiplyBySelectedScalar : Multiply + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmul_lane[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm_index) - /// FMUL Zresult.D, Zop1.D, Zop2.D[imm_index] + /// int64_t svqdecp[_n_s64]_b64(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.D /// - public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmul_lane[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm_index) - /// FMUL Zresult.S, Zop1.S, Zop2.S[imm_index] + /// uint32_t svqdecp[_n_u32]_b64(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.D /// - public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) { throw new PlatformNotSupportedException(); } - - /// MultiplyExtended : Multiply extended (∞×0=2) + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svmulx[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmulx[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmulx[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMULX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// uint64_t svqdecp[_n_u64]_b64(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.D /// - public static unsafe Vector MultiplyExtended(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svfloat32_t svmulx[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmulx[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmulx[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMULX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// svuint64_t svqdecp[_u64](svuint64_t op, svbool_t pg) + /// UQDECP Ztied.D, Pg /// - public static unsafe Vector MultiplyExtended(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } - /// MultiplySubtract : Multiply-subtract, minuend first - /// - /// svuint8_t svmls[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmls[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmls[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B - /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// Saturating increment by number of halfword elements /// - /// svint16_t svmls[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmls[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmls[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// int32_t svqinch_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe int SaturatingIncrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svmls[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmls[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmls[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// int64_t svqinch_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svmls[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmls[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmls[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// uint32_t svqinch_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe uint SaturatingIncrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint8_t svmls[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmls[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmls[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// uint64_t svqinch_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svmls[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmls[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmls[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// svint16_t svqinch_pat[_s16](svint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Ztied.H, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQINCH Zresult.H, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svmls[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmls[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmls[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// svuint16_t svqinch_pat[_u16](svuint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Ztied.H, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQINCH Zresult.H, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating increment by number of word elements /// - /// svuint64_t svmls[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmls[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmls[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// int32_t svqincw_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } - - /// Negate : Negate + public static unsafe int SaturatingIncrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svfloat64_t svneg[_f64]_m(svfloat64_t inactive, svbool_t pg, svfloat64_t op) - /// svfloat64_t svneg[_f64]_x(svbool_t pg, svfloat64_t op) - /// svfloat64_t svneg[_f64]_z(svbool_t pg, svfloat64_t op) - /// FNEG Ztied.D, Pg/M, Zop.D + /// int64_t svqincw_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svneg[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) - /// svint16_t svneg[_s16]_x(svbool_t pg, svint16_t op) - /// svint16_t svneg[_s16]_z(svbool_t pg, svint16_t op) - /// NEG Ztied.H, Pg/M, Zop.H + /// uint32_t svqincw_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe uint SaturatingIncrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svneg[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) - /// svint32_t svneg[_s32]_x(svbool_t pg, svint32_t op) - /// svint32_t svneg[_s32]_z(svbool_t pg, svint32_t op) - /// NEG Ztied.S, Pg/M, Zop.S + /// uint64_t svqincw_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svneg[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) - /// svint64_t svneg[_s64]_x(svbool_t pg, svint64_t op) - /// svint64_t svneg[_s64]_z(svbool_t pg, svint64_t op) - /// NEG Ztied.D, Pg/M, Zop.D + /// svint32_t svqincw_pat[_s32](svint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Ztied.S, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQINCW Zresult.S, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint8_t svneg[_s8]_x(svbool_t pg, svint8_t op) - /// svint8_t svneg[_s8]_m(svint8_t inactive, svbool_t pg, svint8_t op) - /// svint8_t svneg[_s8]_z(svbool_t pg, svint8_t op) - /// NEG Ztied.B, Pg/M, Zop.B + /// svuint32_t svqincw_pat[_u32](svuint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Ztied.S, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQINCW Zresult.S, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating increment by number of doubleword elements /// - /// svfloat32_t svneg[_f32]_m(svfloat32_t inactive, svbool_t pg, svfloat32_t op) - /// svfloat32_t svneg[_f32]_x(svbool_t pg, svfloat32_t op) - /// svfloat32_t svneg[_f32]_z(svbool_t pg, svfloat32_t op) - /// FNEG Ztied.S, Pg/M, Zop.S + /// int32_t svqincd_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe int SaturatingIncrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } - /// Or : Bitwise inclusive OR + /// + /// int64_t svqincd_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingIncrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svorr[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svorr[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svorr[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint32_t svqincd_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe uint SaturatingIncrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint16_t svorr[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svorr[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svorr[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint64_t svqincd_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint32_t svorr[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svorr[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svorr[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// svint64_t svqincd_pat[_s64](svint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Ztied.D, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; SQINCD Zresult.D, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svint64_t svorr[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svorr[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svorr[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// svuint64_t svqincd_pat[_u64](svuint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Ztied.D, pattern, MUL #imm_factor + /// MOVPRFX Zresult, Zop; UQINCD Zresult.D, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } + + + /// Saturating increment by number of byte elements /// - /// svint8_t svorr[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svorr[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svorr[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int32_t svqincb_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCB Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe int SaturatingIncrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svorr[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svorr[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svorr[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int64_t svqincb_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svorr[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svorr[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svorr[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint32_t svqincb_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCB Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe uint SaturatingIncrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svorr[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svorr[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svorr[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint64_t svqincb_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw new PlatformNotSupportedException(); } - /// OrAcross : Bitwise inclusive OR reduction to scalar + /// Saturating increment by active element count /// - /// uint8_t svorv[_u8](svbool_t pg, svuint8_t op) - /// ORV Bresult, Pg, Zop.B + /// int32_t svqincp[_n_s32]_b8(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.B, Wtied /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// int16_t svorv[_s16](svbool_t pg, svint16_t op) - /// ORV Hresult, Pg, Zop.H + /// int64_t svqincp[_n_s64]_b8(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.B /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// int32_t svorv[_s32](svbool_t pg, svint32_t op) - /// ORV Sresult, Pg, Zop.S + /// uint32_t svqincp[_n_u32]_b8(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.B /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// int64_t svorv[_s64](svbool_t pg, svint64_t op) - /// ORV Dresult, Pg, Zop.D + /// uint64_t svqincp[_n_u64]_b8(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.B /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// int8_t svorv[_s8](svbool_t pg, svint8_t op) - /// ORV Bresult, Pg, Zop.B + /// svint16_t svqincp[_s16](svint16_t op, svbool_t pg) + /// SQINCP Ztied.H, Pg /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// uint16_t svorv[_u16](svbool_t pg, svuint16_t op) - /// ORV Hresult, Pg, Zop.H + /// svint32_t svqincp[_s32](svint32_t op, svbool_t pg) + /// SQINCP Ztied.S, Pg /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// uint32_t svorv[_u32](svbool_t pg, svuint32_t op) - /// ORV Sresult, Pg, Zop.S + /// svint64_t svqincp[_s64](svint64_t op, svbool_t pg) + /// SQINCP Ztied.D, Pg /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// uint64_t svorv[_u64](svbool_t pg, svuint64_t op) - /// ORV Dresult, Pg, Zop.D + /// int32_t svqincp[_n_s32]_b16(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.H, Wtied /// - public static unsafe Vector OrAcross(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } + /// + /// int64_t svqincp[_n_s64]_b16(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.H + /// + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } - /// Count nonzero bits + /// + /// uint32_t svqincp[_n_u32]_b16(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.H + /// + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svcnt[_s8]_m(svuint8_t inactive, svbool_t pg, svint8_t op) - /// svuint8_t svcnt[_s8]_x(svbool_t pg, svint8_t op) - /// svuint8_t svcnt[_s8]_z(svbool_t pg, svint8_t op) - /// CNT Ztied.B, Pg/M, Zop.B + /// uint64_t svqincp[_n_u64]_b16(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.H /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint8_t svcnt[_u8]_m(svuint8_t inactive, svbool_t pg, svuint8_t op) - /// svuint8_t svcnt[_u8]_x(svbool_t pg, svuint8_t op) - /// svuint8_t svcnt[_u8]_z(svbool_t pg, svuint8_t op) - /// CNT Ztied.B, Pg/M, Zop.B + /// svuint16_t svqincp[_u16](svuint16_t op, svbool_t pg) + /// UQINCP Ztied.H, Pg /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svcnt[_s16]_m(svuint16_t inactive, svbool_t pg, svint16_t op) - /// svuint16_t svcnt[_s16]_x(svbool_t pg, svint16_t op) - /// svuint16_t svcnt[_s16]_z(svbool_t pg, svint16_t op) - /// CNT Ztied.H, Pg/M, Zop.H + /// int32_t svqincp[_n_s32]_b32(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.S, Wtied /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint16_t svcnt[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) - /// svuint16_t svcnt[_u16]_x(svbool_t pg, svuint16_t op) - /// svuint16_t svcnt[_u16]_z(svbool_t pg, svuint16_t op) - /// CNT Ztied.H, Pg/M, Zop.H + /// int64_t svqincp[_n_s64]_b32(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svcnt[_s32]_m(svuint32_t inactive, svbool_t pg, svint32_t op) - /// svuint32_t svcnt[_s32]_x(svbool_t pg, svint32_t op) - /// svuint32_t svcnt[_s32]_z(svbool_t pg, svint32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// uint32_t svqincp[_n_u32]_b32(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svcnt[_f32]_m(svuint32_t inactive, svbool_t pg, svfloat32_t op) - /// svuint32_t svcnt[_f32]_x(svbool_t pg, svfloat32_t op) - /// svuint32_t svcnt[_f32]_z(svbool_t pg, svfloat32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// uint64_t svqincp[_n_u64]_b32(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint32_t svcnt[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) - /// svuint32_t svcnt[_u32]_x(svbool_t pg, svuint32_t op) - /// svuint32_t svcnt[_u32]_z(svbool_t pg, svuint32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// svuint32_t svqincp[_u32](svuint32_t op, svbool_t pg) + /// UQINCP Ztied.S, Pg /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svcnt[_f64]_m(svuint64_t inactive, svbool_t pg, svfloat64_t op) - /// svuint64_t svcnt[_f64]_x(svbool_t pg, svfloat64_t op) - /// svuint64_t svcnt[_f64]_z(svbool_t pg, svfloat64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// int32_t svqincp[_n_s32]_b64(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.D, Wtied /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svcnt[_s64]_m(svuint64_t inactive, svbool_t pg, svint64_t op) - /// svuint64_t svcnt[_s64]_x(svbool_t pg, svint64_t op) - /// svuint64_t svcnt[_s64]_z(svbool_t pg, svint64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// int64_t svqincp[_n_s64]_b64(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.D /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) { throw new PlatformNotSupportedException(); } /// - /// svuint64_t svcnt[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) - /// svuint64_t svcnt[_u64]_x(svbool_t pg, svuint64_t op) - /// svuint64_t svcnt[_u64]_z(svbool_t pg, svuint64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// uint32_t svqincp[_n_u32]_b64(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.D /// - public static unsafe Vector PopCount(Vector value) { throw new PlatformNotSupportedException(); } + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svqincp[_n_u64]_b64(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.D + /// + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svqincp[_u64](svuint64_t op, svbool_t pg) + /// UQINCP Ztied.D, Pg + /// + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) { throw new PlatformNotSupportedException(); } /// SignExtend16 : Sign-extend the low 16 bits @@ -3074,6 +4351,69 @@ internal Arm64() { } public static unsafe void StoreNarrowing(Vector mask, uint* address, Vector data) { throw new PlatformNotSupportedException(); } + /// Non-truncating store, non-temporal + + /// + /// void svstnt1[_u8](svbool_t pg, uint8_t *base, svuint8_t data) + /// STNT1B Zdata.B, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, byte* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_f64](svbool_t pg, float64_t *base, svfloat64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, double* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_s16](svbool_t pg, int16_t *base, svint16_t data) + /// STNT1H Zdata.H, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, short* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_s32](svbool_t pg, int32_t *base, svint32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, int* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_s64](svbool_t pg, int64_t *base, svint64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, long* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_s8](svbool_t pg, int8_t *base, svint8_t data) + /// STNT1B Zdata.B, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, sbyte* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_f32](svbool_t pg, float32_t *base, svfloat32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, float* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_u16](svbool_t pg, uint16_t *base, svuint16_t data) + /// STNT1H Zdata.H, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, ushort* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_u32](svbool_t pg, uint32_t *base, svuint32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, uint* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// void svstnt1[_u64](svbool_t pg, uint64_t *base, svuint64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, ulong* address, Vector data) { throw new PlatformNotSupportedException(); } + + /// Subtract : Subtract /// @@ -3291,6 +4631,132 @@ internal Arm64() { } public static unsafe Vector SignExtendWideningUpper(Vector value) { throw new PlatformNotSupportedException(); } + /// Interleave even elements from two inputs + + /// + /// svuint8_t svtrn1[_u8](svuint8_t op1, svuint8_t op2) + /// TRN1 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svtrn1[_f64](svfloat64_t op1, svfloat64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svtrn1[_s16](svint16_t op1, svint16_t op2) + /// TRN1 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svtrn1[_s32](svint32_t op1, svint32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svtrn1[_s64](svint64_t op1, svint64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svtrn1[_s8](svint8_t op1, svint8_t op2) + /// TRN1 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svtrn1[_f32](svfloat32_t op1, svfloat32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svtrn1[_u16](svuint16_t op1, svuint16_t op2) + /// TRN1 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svtrn1[_u32](svuint32_t op1, svuint32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svtrn1[_u64](svuint64_t op1, svuint64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// Interleave odd elements from two inputs + + /// + /// svuint8_t svtrn2[_u8](svuint8_t op1, svuint8_t op2) + /// TRN2 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svtrn2[_f64](svfloat64_t op1, svfloat64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svtrn2[_s16](svint16_t op1, svint16_t op2) + /// TRN2 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svtrn2[_s32](svint32_t op1, svint32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svtrn2[_s64](svint64_t op1, svint64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svtrn2[_s8](svint8_t op1, svint8_t op2) + /// TRN2 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svtrn2[_f32](svfloat32_t op1, svfloat32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svtrn2[_u16](svuint16_t op1, svuint16_t op2) + /// TRN2 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svtrn2[_u32](svuint32_t op1, svuint32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svtrn2[_u64](svuint64_t op1, svuint64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// UnzipEven : Concatenate even elements from two inputs /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index 251ae1f45a2e4..8295f37ba4083 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -669,7 +669,153 @@ internal Arm64() { } /// public static unsafe Vector BooleanNot(Vector value) => BooleanNot(value); + /// Compute vector addresses for 16-bit data + /// + /// svuint32_t svadrh[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) => Compute16BitAddresses(bases, indices); + + /// + /// svuint32_t svadrh[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) => Compute16BitAddresses(bases, indices); + + /// + /// svuint64_t svadrh[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) => Compute16BitAddresses(bases, indices); + + /// + /// svuint64_t svadrh[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #1] + /// + public static unsafe Vector Compute16BitAddresses(Vector bases, Vector indices) => Compute16BitAddresses(bases, indices); + + + /// Compute vector addresses for 32-bit data + + /// + /// svuint32_t svadrw[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) => Compute32BitAddresses(bases, indices); + + /// + /// svuint32_t svadrw[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) => Compute32BitAddresses(bases, indices); + + /// + /// svuint64_t svadrw[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) => Compute32BitAddresses(bases, indices); + + /// + /// svuint64_t svadrw[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #2] + /// + public static unsafe Vector Compute32BitAddresses(Vector bases, Vector indices) => Compute32BitAddresses(bases, indices); + + + /// Compute vector addresses for 64-bit data + + /// + /// svuint32_t svadrd[_u32base]_[s32]index(svuint32_t bases, svint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) => Compute64BitAddresses(bases, indices); + + /// + /// svuint32_t svadrd[_u32base]_[u32]index(svuint32_t bases, svuint32_t indices) + /// ADR Zresult.S, [Zbases.S, Zindices.S, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) => Compute64BitAddresses(bases, indices); + + /// + /// svuint64_t svadrd[_u64base]_[s64]index(svuint64_t bases, svint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) => Compute64BitAddresses(bases, indices); + + /// + /// svuint64_t svadrd[_u64base]_[u64]index(svuint64_t bases, svuint64_t indices) + /// ADR Zresult.D, [Zbases.D, Zindices.D, LSL #3] + /// + public static unsafe Vector Compute64BitAddresses(Vector bases, Vector indices) => Compute64BitAddresses(bases, indices); + + + /// Compute vector addresses for 8-bit data + + /// + /// svuint32_t svadrb[_u32base]_[s32]offset(svuint32_t bases, svint32_t offsets) + /// ADR Zresult.S, [Zbases.S, Zoffsets.S] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); + + /// + /// svuint32_t svadrb[_u32base]_[u32]offset(svuint32_t bases, svuint32_t offsets) + /// ADR Zresult.S, [Zbases.S, Zoffsets.S] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); + + /// + /// svuint64_t svadrb[_u64base]_[s64]offset(svuint64_t bases, svint64_t offsets) + /// ADR Zresult.D, [Zbases.D, Zoffsets.D] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); + + /// + /// svuint64_t svadrb[_u64base]_[u64]offset(svuint64_t bases, svuint64_t offsets) + /// ADR Zresult.D, [Zbases.D, Zoffsets.D] + /// + public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); + + /// Shuffle active elements of vector to the right and fill with zero + + /// + /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + + /// Compare equal to /// ConditionalSelect : Conditionally select elements /// @@ -1376,6 +1522,69 @@ internal Arm64() { } public static unsafe Vector FusedMultiplySubtractNegated(Vector minuend, Vector left, Vector right) => FusedMultiplySubtractNegated(minuend, left, right); + /// Count set predicate bits + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b8(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.B + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b16(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.H + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b32(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.S + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// + /// uint64_t svcntp_b64(svbool_t pg, svbool_t op) + /// CNTP Xresult, Pg, Pop.D + /// + public static unsafe ulong GetActiveElementCount(Vector mask, Vector from) => GetActiveElementCount(mask, from); + + /// LeadingSignCount : Count leading sign bits /// @@ -1766,969 +1975,2026 @@ internal Arm64() { } /// public static unsafe Vector LoadVectorUInt32ZeroExtendToUInt64(Vector mask, uint* address) => LoadVectorUInt32ZeroExtendToUInt64(mask, address); - /// Max : Maximum + /// + /// svint16_t svldnf1ub_s16(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt16(byte* address) => LoadVectorByteNonFaultingZeroExtendToInt16(address); /// - /// svuint8_t svmax[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmax[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmax[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// UMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// UMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svint32_t svldnf1ub_s32(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt32(byte* address) => LoadVectorByteNonFaultingZeroExtendToInt32(address); /// - /// svfloat64_t svmax[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmax[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmax[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint64_t svldnf1ub_s64(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToInt64(byte* address) => LoadVectorByteNonFaultingZeroExtendToInt64(address); /// - /// svint16_t svmax[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmax[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmax[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// SMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// SMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint16_t svldnf1ub_u16(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.H, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt16(byte* address) => LoadVectorByteNonFaultingZeroExtendToUInt16(address); /// - /// svint32_t svmax[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svmax[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svmax[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// SMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// SMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint32_t svldnf1ub_u32(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt32(byte* address) => LoadVectorByteNonFaultingZeroExtendToUInt32(address); /// - /// svint64_t svmax[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmax[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmax[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// SMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// SMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint64_t svldnf1ub_u64(svbool_t pg, const uint8_t *base) + /// LDNF1B Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorByteNonFaultingZeroExtendToUInt64(byte* address) => LoadVectorByteNonFaultingZeroExtendToUInt64(address); /// - /// svint8_t svmax[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmax[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmax[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// SMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// SMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svint32_t svldnf1uh_s32(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToInt32(ushort* address) => LoadVectorUInt16NonFaultingZeroExtendToInt32(address); /// - /// svfloat32_t svmax[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmax[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmax[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svint64_t svldnf1uh_s64(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToInt64(ushort* address) => LoadVectorUInt16NonFaultingZeroExtendToInt64(address); /// - /// svuint16_t svmax[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmax[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmax[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// UMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// UMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint32_t svldnf1uh_u32(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.S, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToUInt32(ushort* address) => LoadVectorUInt16NonFaultingZeroExtendToUInt32(address); /// - /// svuint32_t svmax[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmax[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmax[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// UMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// UMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint64_t svldnf1uh_u64(svbool_t pg, const uint16_t *base) + /// LDNF1H Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorUInt16NonFaultingZeroExtendToUInt64(ushort* address) => LoadVectorUInt16NonFaultingZeroExtendToUInt64(address); /// - /// svuint64_t svmax[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmax[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmax[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// UMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// UMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint64_t svldnf1uw_s64(svbool_t pg, const uint32_t *base) + /// LDNF1W Zresult.D, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + public static unsafe Vector LoadVectorUInt32NonFaultingZeroExtendToInt64(uint* address) => LoadVectorUInt32NonFaultingZeroExtendToInt64(address); + /// + /// svuint64_t svldnf1uw_u64(svbool_t pg, const uint32_t *base) + /// LDNF1W Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorUInt32NonFaultingZeroExtendToUInt64(uint* address) => LoadVectorUInt32NonFaultingZeroExtendToUInt64(address); - /// MaxAcross : Maximum reduction to scalar + /// + /// svuint8x2_t svld2[_u8](svbool_t pg, const uint8_t *base) + /// LD2B {Zresult0.B, Zresult1.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, byte* address) => Load2xVectorAndUnzip(mask, address); /// - /// uint8_t svmaxv[_u8](svbool_t pg, svuint8_t op) - /// UMAXV Bresult, Pg, Zop.B + /// svfloat64x2_t svld2[_f64](svbool_t pg, const float64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, double* address) => Load2xVectorAndUnzip(mask, address); /// - /// float64_t svmaxv[_f64](svbool_t pg, svfloat64_t op) - /// FMAXV Dresult, Pg, Zop.D + /// svint16x2_t svld2[_s16](svbool_t pg, const int16_t *base) + /// LD2H {Zresult0.H, Zresult1.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, short* address) => Load2xVectorAndUnzip(mask, address); /// - /// int16_t svmaxv[_s16](svbool_t pg, svint16_t op) - /// SMAXV Hresult, Pg, Zop.H + /// svint32x2_t svld2[_s32](svbool_t pg, const int32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, int* address) => Load2xVectorAndUnzip(mask, address); /// - /// int32_t svmaxv[_s32](svbool_t pg, svint32_t op) - /// SMAXV Sresult, Pg, Zop.S + /// svint64x2_t svld2[_s64](svbool_t pg, const int64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, long* address) => Load2xVectorAndUnzip(mask, address); /// - /// int64_t svmaxv[_s64](svbool_t pg, svint64_t op) - /// SMAXV Dresult, Pg, Zop.D + /// svint8x2_t svld2[_s8](svbool_t pg, const int8_t *base) + /// LD2B {Zresult0.B, Zresult1.B}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, sbyte* address) => Load2xVectorAndUnzip(mask, address); /// - /// int8_t svmaxv[_s8](svbool_t pg, svint8_t op) - /// SMAXV Bresult, Pg, Zop.B + /// svfloat32x2_t svld2[_f32](svbool_t pg, const float32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, float* address) => Load2xVectorAndUnzip(mask, address); /// - /// float32_t svmaxv[_f32](svbool_t pg, svfloat32_t op) - /// FMAXV Sresult, Pg, Zop.S + /// svuint16x2_t svld2[_u16](svbool_t pg, const uint16_t *base) + /// LD2H {Zresult0.H, Zresult1.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, ushort* address) => Load2xVectorAndUnzip(mask, address); /// - /// uint16_t svmaxv[_u16](svbool_t pg, svuint16_t op) - /// UMAXV Hresult, Pg, Zop.H + /// svuint32x2_t svld2[_u32](svbool_t pg, const uint32_t *base) + /// LD2W {Zresult0.S, Zresult1.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, uint* address) => Load2xVectorAndUnzip(mask, address); /// - /// uint32_t svmaxv[_u32](svbool_t pg, svuint32_t op) - /// UMAXV Sresult, Pg, Zop.S + /// svuint64x2_t svld2[_u64](svbool_t pg, const uint64_t *base) + /// LD2D {Zresult0.D, Zresult1.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector) Load2xVectorAndUnzip(Vector mask, ulong* address) => Load2xVectorAndUnzip(mask, address); /// - /// uint64_t svmaxv[_u64](svbool_t pg, svuint64_t op) - /// UMAXV Dresult, Pg, Zop.D + /// svuint8x3_t svld3[_u8](svbool_t pg, const uint8_t *base) + /// LD3B {Zresult0.B - Zresult2.B}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, byte* address) => Load3xVectorAndUnzip(mask, address); + /// + /// svfloat64x3_t svld3[_f64](svbool_t pg, const float64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, double* address) => Load3xVectorAndUnzip(mask, address); - /// MaxNumber : Maximum number + /// + /// svint16x3_t svld3[_s16](svbool_t pg, const int16_t *base) + /// LD3H {Zresult0.H - Zresult2.H}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, short* address) => Load3xVectorAndUnzip(mask, address); /// - /// svfloat64_t svmaxnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmaxnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmaxnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMAXNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMAXNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint32x3_t svld3[_s32](svbool_t pg, const int32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumber(Vector left, Vector right) => MaxNumber(left, right); + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, int* address) => Load3xVectorAndUnzip(mask, address); /// - /// svfloat32_t svmaxnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmaxnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmaxnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMAXNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMAXNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svint64x3_t svld3[_s64](svbool_t pg, const int64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumber(Vector left, Vector right) => MaxNumber(left, right); + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, long* address) => Load3xVectorAndUnzip(mask, address); + /// + /// svint8x3_t svld3[_s8](svbool_t pg, const int8_t *base) + /// LD3B {Zresult0.B - Zresult2.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, sbyte* address) => Load3xVectorAndUnzip(mask, address); - /// MaxNumberAcross : Maximum number reduction to scalar + /// + /// svfloat32x3_t svld3[_f32](svbool_t pg, const float32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, float* address) => Load3xVectorAndUnzip(mask, address); /// - /// float64_t svmaxnmv[_f64](svbool_t pg, svfloat64_t op) - /// FMAXNMV Dresult, Pg, Zop.D + /// svuint16x3_t svld3[_u16](svbool_t pg, const uint16_t *base) + /// LD3H {Zresult0.H - Zresult2.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumberAcross(Vector value) => MaxNumberAcross(value); + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, ushort* address) => Load3xVectorAndUnzip(mask, address); /// - /// float32_t svmaxnmv[_f32](svbool_t pg, svfloat32_t op) - /// FMAXNMV Sresult, Pg, Zop.S + /// svuint32x3_t svld3[_u32](svbool_t pg, const uint32_t *base) + /// LD3W {Zresult0.S - Zresult2.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector MaxNumberAcross(Vector value) => MaxNumberAcross(value); + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, uint* address) => Load3xVectorAndUnzip(mask, address); + /// + /// svuint64x3_t svld3[_u64](svbool_t pg, const uint64_t *base) + /// LD3D {Zresult0.D - Zresult2.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector) Load3xVectorAndUnzip(Vector mask, ulong* address) => Load3xVectorAndUnzip(mask, address); - /// Min : Minimum + /// + /// svuint8x4_t svld4[_u8](svbool_t pg, const uint8_t *base) + /// LD4B {Zresult0.B - Zresult3.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, byte* address) => Load4xVectorAndUnzip(mask, address); /// - /// svuint8_t svmin[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmin[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svmin[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// UMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// UMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svfloat64x4_t svld4[_f64](svbool_t pg, const float64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, double* address) => Load4xVectorAndUnzip(mask, address); /// - /// svfloat64_t svmin[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmin[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmin[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svint16x4_t svld4[_s16](svbool_t pg, const int16_t *base) + /// LD4H {Zresult0.H - Zresult3.H}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, short* address) => Load4xVectorAndUnzip(mask, address); /// - /// svint16_t svmin[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmin[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svmin[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// SMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// SMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svint32x4_t svld4[_s32](svbool_t pg, const int32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, int* address) => Load4xVectorAndUnzip(mask, address); /// - /// svint32_t svmin[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint64x4_t svld4[_s64](svbool_t pg, const int64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, long* address) => Load4xVectorAndUnzip(mask, address); + + /// + /// svint8x4_t svld4[_s8](svbool_t pg, const int8_t *base) + /// LD4B {Zresult0.B - Zresult3.B}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, sbyte* address) => Load4xVectorAndUnzip(mask, address); + + /// + /// svfloat32x4_t svld4[_f32](svbool_t pg, const float32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, float* address) => Load4xVectorAndUnzip(mask, address); + + /// + /// svuint16x4_t svld4[_u16](svbool_t pg, const uint16_t *base) + /// LD4H {Zresult0.H - Zresult3.H}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, ushort* address) => Load4xVectorAndUnzip(mask, address); + + /// + /// svuint32x4_t svld4[_u32](svbool_t pg, const uint32_t *base) + /// LD4W {Zresult0.S - Zresult3.S}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, uint* address) => Load4xVectorAndUnzip(mask, address); + + /// + /// svuint64x4_t svld4[_u64](svbool_t pg, const uint64_t *base) + /// LD4D {Zresult0.D - Zresult3.D}, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe (Vector, Vector, Vector, Vector) Load4xVectorAndUnzip(Vector mask, ulong* address) => Load4xVectorAndUnzip(mask, address); + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svint32_t svldnf1sh_s32(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToInt32(short* address) => LoadVectorInt16NonFaultingSignExtendToInt32(address); + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sh_s64(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToInt64(short* address) => LoadVectorInt16NonFaultingSignExtendToInt64(address); + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svuint32_t svldnf1sh_u32(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToUInt32(short* address) => LoadVectorInt16NonFaultingSignExtendToUInt32(address); + + + /// Load 16-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sh_u64(svbool_t pg, const int16_t *base) + /// LDNF1SH Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt16NonFaultingSignExtendToUInt64(short* address) => LoadVectorInt16NonFaultingSignExtendToUInt64(address); + + /// Load 32-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sw_s64(svbool_t pg, const int32_t *base) + /// LDNF1SW Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt32NonFaultingSignExtendToInt64(int* address) => LoadVectorInt32NonFaultingSignExtendToInt64(address); + + /// Load 32-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sw_u64(svbool_t pg, const int32_t *base) + /// LDNF1SW Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorInt32NonFaultingSignExtendToUInt64(int* address) => LoadVectorInt32NonFaultingSignExtendToUInt64(address); + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint16_t svldnf1sb_s16(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt16(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToInt16(address); + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint32_t svldnf1sb_s32(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt32(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToInt32(address); + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svint64_t svldnf1sb_s64(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToInt64(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToInt64(address); + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint16_t svldnf1sb_u16(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.H, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt16(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToUInt16(address); + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint32_t svldnf1sb_u32(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.S, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt32(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToUInt32(address); + + + /// Load 8-bit data and sign-extend, non-faulting + + /// + /// svuint64_t svldnf1sb_u64(svbool_t pg, const int8_t *base) + /// LDNF1SB Zresult.D, Pg/Z, [Xbase, #0, MUL VL] + /// + public static unsafe Vector LoadVectorSByteNonFaultingSignExtendToUInt64(sbyte* address) => LoadVectorSByteNonFaultingSignExtendToUInt64(address); + + /// Max : Maximum + + /// + /// svuint8_t svmax[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmax[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmax[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// UMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// UMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svfloat64_t svmax[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmax[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmax[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svint16_t svmax[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmax[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmax[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// SMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// SMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svint32_t svmax[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svmax[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svmax[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// SMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// SMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svint64_t svmax[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmax[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmax[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// SMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// SMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svint8_t svmax[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmax[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmax[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// SMAX Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// SMAX Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svfloat32_t svmax[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmax[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmax[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svuint16_t svmax[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmax[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmax[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// UMAX Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// UMAX Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svuint32_t svmax[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmax[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmax[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// UMAX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// UMAX Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + /// + /// svuint64_t svmax[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmax[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmax[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// UMAX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// UMAX Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Max(Vector left, Vector right) => Max(left, right); + + + /// MaxAcross : Maximum reduction to scalar + + /// + /// uint8_t svmaxv[_u8](svbool_t pg, svuint8_t op) + /// UMAXV Bresult, Pg, Zop.B + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// float64_t svmaxv[_f64](svbool_t pg, svfloat64_t op) + /// FMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// int16_t svmaxv[_s16](svbool_t pg, svint16_t op) + /// SMAXV Hresult, Pg, Zop.H + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// int32_t svmaxv[_s32](svbool_t pg, svint32_t op) + /// SMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// int64_t svmaxv[_s64](svbool_t pg, svint64_t op) + /// SMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// int8_t svmaxv[_s8](svbool_t pg, svint8_t op) + /// SMAXV Bresult, Pg, Zop.B + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// float32_t svmaxv[_f32](svbool_t pg, svfloat32_t op) + /// FMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// uint16_t svmaxv[_u16](svbool_t pg, svuint16_t op) + /// UMAXV Hresult, Pg, Zop.H + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// uint32_t svmaxv[_u32](svbool_t pg, svuint32_t op) + /// UMAXV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + /// + /// uint64_t svmaxv[_u64](svbool_t pg, svuint64_t op) + /// UMAXV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxAcross(Vector value) => MaxAcross(value); + + + /// MaxNumber : Maximum number + + /// + /// svfloat64_t svmaxnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmaxnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmaxnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMAXNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMAXNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector MaxNumber(Vector left, Vector right) => MaxNumber(left, right); + + /// + /// svfloat32_t svmaxnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmaxnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmaxnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMAXNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMAXNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector MaxNumber(Vector left, Vector right) => MaxNumber(left, right); + + + /// MaxNumberAcross : Maximum number reduction to scalar + + /// + /// float64_t svmaxnmv[_f64](svbool_t pg, svfloat64_t op) + /// FMAXNMV Dresult, Pg, Zop.D + /// + public static unsafe Vector MaxNumberAcross(Vector value) => MaxNumberAcross(value); + + /// + /// float32_t svmaxnmv[_f32](svbool_t pg, svfloat32_t op) + /// FMAXNMV Sresult, Pg, Zop.S + /// + public static unsafe Vector MaxNumberAcross(Vector value) => MaxNumberAcross(value); + + + /// Min : Minimum + + /// + /// svuint8_t svmin[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmin[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svmin[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// UMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// UMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svfloat64_t svmin[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmin[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmin[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svint16_t svmin[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmin[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svmin[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// SMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// SMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svint32_t svmin[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) /// svint32_t svmin[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) /// svint32_t svmin[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) /// SMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S /// SMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svint64_t svmin[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmin[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svmin[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// SMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// SMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svint8_t svmin[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmin[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svmin[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// SMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// SMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svfloat32_t svmin[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmin[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmin[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svuint16_t svmin[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmin[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svmin[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// UMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// UMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svuint32_t svmin[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmin[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svmin[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// UMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// UMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + /// + /// svuint64_t svmin[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmin[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svmin[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// UMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// UMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + + + /// MinAcross : Minimum reduction to scalar + + /// + /// uint8_t svminv[_u8](svbool_t pg, svuint8_t op) + /// UMINV Bresult, Pg, Zop.B + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// float64_t svminv[_f64](svbool_t pg, svfloat64_t op) + /// FMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// int16_t svminv[_s16](svbool_t pg, svint16_t op) + /// SMINV Hresult, Pg, Zop.H + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// int32_t svminv[_s32](svbool_t pg, svint32_t op) + /// SMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// int64_t svminv[_s64](svbool_t pg, svint64_t op) + /// SMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// int8_t svminv[_s8](svbool_t pg, svint8_t op) + /// SMINV Bresult, Pg, Zop.B + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// float32_t svminv[_f32](svbool_t pg, svfloat32_t op) + /// FMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// uint16_t svminv[_u16](svbool_t pg, svuint16_t op) + /// UMINV Hresult, Pg, Zop.H + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// uint32_t svminv[_u32](svbool_t pg, svuint32_t op) + /// UMINV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + /// + /// uint64_t svminv[_u64](svbool_t pg, svuint64_t op) + /// UMINV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + + + /// MinNumber : Minimum number + + /// + /// svfloat64_t svminnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svminnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svminnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMINNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMINNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// + public static unsafe Vector MinNumber(Vector left, Vector right) => MinNumber(left, right); + + /// + /// svfloat32_t svminnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svminnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svminnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMINNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMINNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// + public static unsafe Vector MinNumber(Vector left, Vector right) => MinNumber(left, right); + + + /// MinNumberAcross : Minimum number reduction to scalar + + /// + /// float64_t svminnmv[_f64](svbool_t pg, svfloat64_t op) + /// FMINNMV Dresult, Pg, Zop.D + /// + public static unsafe Vector MinNumberAcross(Vector value) => MinNumberAcross(value); + + /// + /// float32_t svminnmv[_f32](svbool_t pg, svfloat32_t op) + /// FMINNMV Sresult, Pg, Zop.S + /// + public static unsafe Vector MinNumberAcross(Vector value) => MinNumberAcross(value); + + + /// + /// svint8_t svmul[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svint8_t svmul[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svint8_t svmul[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svint16_t svmul[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svint16_t svmul[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svint16_t svmul[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svint32_t svmul[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svint32_t svmul[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svint32_t svmul[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svint64_t svmul[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svint64_t svmul[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svint64_t svmul[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svuint8_t svmul[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svuint8_t svmul[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// svuint8_t svmul[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B + /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svuint16_t svmul[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svuint16_t svmul[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// svuint16_t svmul[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H + /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svuint32_t svmul[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svuint32_t svmul[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svuint32_t svmul[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svuint64_t svmul[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svuint64_t svmul[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svuint64_t svmul[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svfloat32_t svmul[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svfloat32_t svmul[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// FMUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// FMUL Zresult.S, Zop1.S, Zop2.S + /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// svfloat32_t svmul[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S + /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// + /// svfloat64_t svmul[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svfloat64_t svmul[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// FMUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// FMUL Zresult.D, Zop1.D, Zop2.D + /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// svfloat64_t svmul[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D + /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// + public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + + /// MultiplyAdd : Multiply-add, addend first + + /// + /// svuint8_t svmla[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmla[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmla[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svint16_t svmla[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmla[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmla[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svint32_t svmla[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmla[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmla[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svint64_t svmla[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmla[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmla[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svint8_t svmla[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmla[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmla[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svuint16_t svmla[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmla[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmla[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svuint32_t svmla[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmla[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmla[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// + /// svuint64_t svmla[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmla[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmla[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + + /// MultiplyBySelectedScalar : Multiply + + /// + /// svfloat64_t svmul_lane[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm_index) + /// FMUL Zresult.D, Zop1.D, Zop2.D[imm_index] + /// + public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) => MultiplyBySelectedScalar(left, right, rightIndex); + + /// + /// svfloat32_t svmul_lane[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm_index) + /// FMUL Zresult.S, Zop1.S, Zop2.S[imm_index] + /// + public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) => MultiplyBySelectedScalar(left, right, rightIndex); + + /// MultiplyExtended : Multiply extended (∞×0=2) + + /// + /// svfloat64_t svmulx[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmulx[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// svfloat64_t svmulx[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FMULX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// + public static unsafe Vector MultiplyExtended(Vector left, Vector right) => MultiplyExtended(left, right); + + /// + /// svfloat32_t svmulx[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmulx[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// svfloat32_t svmulx[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FMULX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// + public static unsafe Vector MultiplyExtended(Vector left, Vector right) => MultiplyExtended(left, right); + + /// MultiplySubtract : Multiply-subtract, minuend first + + /// + /// svuint8_t svmls[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmls[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// svuint8_t svmls[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) + /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svint16_t svmls[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmls[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// svint16_t svmls[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) + /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svint32_t svmls[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmls[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// svint32_t svmls[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) + /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svint64_t svmls[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmls[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// svint64_t svmls[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) + /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svint8_t svmls[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmls[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// svint8_t svmls[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) + /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svuint16_t svmls[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmls[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// svuint16_t svmls[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) + /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svuint32_t svmls[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmls[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// svuint32_t svmls[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) + /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// + /// svuint64_t svmls[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmls[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// svuint64_t svmls[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) + /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// + public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + + /// Negate : Negate + + /// + /// svfloat64_t svneg[_f64]_m(svfloat64_t inactive, svbool_t pg, svfloat64_t op) + /// svfloat64_t svneg[_f64]_x(svbool_t pg, svfloat64_t op) + /// svfloat64_t svneg[_f64]_z(svbool_t pg, svfloat64_t op) + /// FNEG Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// + /// svint16_t svneg[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) + /// svint16_t svneg[_s16]_x(svbool_t pg, svint16_t op) + /// svint16_t svneg[_s16]_z(svbool_t pg, svint16_t op) + /// NEG Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// + /// svint32_t svneg[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// svint32_t svneg[_s32]_x(svbool_t pg, svint32_t op) + /// svint32_t svneg[_s32]_z(svbool_t pg, svint32_t op) + /// NEG Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// + /// svint64_t svneg[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// svint64_t svneg[_s64]_x(svbool_t pg, svint64_t op) + /// svint64_t svneg[_s64]_z(svbool_t pg, svint64_t op) + /// NEG Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// + /// svint8_t svneg[_s8]_x(svbool_t pg, svint8_t op) + /// svint8_t svneg[_s8]_m(svint8_t inactive, svbool_t pg, svint8_t op) + /// svint8_t svneg[_s8]_z(svbool_t pg, svint8_t op) + /// NEG Ztied.B, Pg/M, Zop.B + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// + /// svfloat32_t svneg[_f32]_m(svfloat32_t inactive, svbool_t pg, svfloat32_t op) + /// svfloat32_t svneg[_f32]_x(svbool_t pg, svfloat32_t op) + /// svfloat32_t svneg[_f32]_z(svbool_t pg, svfloat32_t op) + /// FNEG Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector Negate(Vector value) => Negate(value); + + /// Or : Bitwise inclusive OR + + /// + /// svuint8_t svorr[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svorr[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// svuint8_t svorr[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) + /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svint16_t svorr[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svorr[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) + /// svint16_t svorr[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) + /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svint32_t svorr[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svorr[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) + /// svint32_t svorr[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) + /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svint64_t svorr[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svorr[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) + /// svint64_t svorr[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) + /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svint8_t svorr[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svorr[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) + /// svint8_t svorr[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) + /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svuint16_t svorr[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svorr[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// svuint16_t svorr[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) + /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svuint32_t svorr[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svorr[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// svuint32_t svorr[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) + /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + /// + /// svuint64_t svorr[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svorr[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// svuint64_t svorr[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) + /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// ORR Zresult.D, Zop1.D, Zop2.D + /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) + /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// + public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + + + /// OrAcross : Bitwise inclusive OR reduction to scalar + + /// + /// uint8_t svorv[_u8](svbool_t pg, svuint8_t op) + /// ORV Bresult, Pg, Zop.B + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// int16_t svorv[_s16](svbool_t pg, svint16_t op) + /// ORV Hresult, Pg, Zop.H + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// int32_t svorv[_s32](svbool_t pg, svint32_t op) + /// ORV Sresult, Pg, Zop.S + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// int64_t svorv[_s64](svbool_t pg, svint64_t op) + /// ORV Dresult, Pg, Zop.D + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// int8_t svorv[_s8](svbool_t pg, svint8_t op) + /// ORV Bresult, Pg, Zop.B + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// uint16_t svorv[_u16](svbool_t pg, svuint16_t op) + /// ORV Hresult, Pg, Zop.H + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + /// + /// uint32_t svorv[_u32](svbool_t pg, svuint32_t op) + /// ORV Sresult, Pg, Zop.S + /// + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); /// - /// svint64_t svmin[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmin[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svmin[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// SMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// SMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// uint64_t svorv[_u64](svbool_t pg, svuint64_t op) + /// ORV Dresult, Pg, Zop.D /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + + + /// Count nonzero bits /// - /// svint8_t svmin[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmin[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svmin[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// SMIN Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// SMIN Ztied2.B, Pg/M, Ztied2.B, Zop1.B + /// svuint8_t svcnt[_s8]_m(svuint8_t inactive, svbool_t pg, svint8_t op) + /// svuint8_t svcnt[_s8]_x(svbool_t pg, svint8_t op) + /// svuint8_t svcnt[_s8]_z(svbool_t pg, svint8_t op) + /// CNT Ztied.B, Pg/M, Zop.B /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// svfloat32_t svmin[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmin[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmin[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint8_t svcnt[_u8]_m(svuint8_t inactive, svbool_t pg, svuint8_t op) + /// svuint8_t svcnt[_u8]_x(svbool_t pg, svuint8_t op) + /// svuint8_t svcnt[_u8]_z(svbool_t pg, svuint8_t op) + /// CNT Ztied.B, Pg/M, Zop.B /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// svuint16_t svmin[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmin[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svmin[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// UMIN Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// UMIN Ztied2.H, Pg/M, Ztied2.H, Zop1.H + /// svuint16_t svcnt[_s16]_m(svuint16_t inactive, svbool_t pg, svint16_t op) + /// svuint16_t svcnt[_s16]_x(svbool_t pg, svint16_t op) + /// svuint16_t svcnt[_s16]_z(svbool_t pg, svint16_t op) + /// CNT Ztied.H, Pg/M, Zop.H /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// svuint32_t svmin[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmin[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svmin[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// UMIN Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// UMIN Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// svuint16_t svcnt[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) + /// svuint16_t svcnt[_u16]_x(svbool_t pg, svuint16_t op) + /// svuint16_t svcnt[_u16]_z(svbool_t pg, svuint16_t op) + /// CNT Ztied.H, Pg/M, Zop.H /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// svuint64_t svmin[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmin[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svmin[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// UMIN Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// UMIN Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint32_t svcnt[_s32]_m(svuint32_t inactive, svbool_t pg, svint32_t op) + /// svuint32_t svcnt[_s32]_x(svbool_t pg, svint32_t op) + /// svuint32_t svcnt[_s32]_z(svbool_t pg, svint32_t op) + /// CNT Ztied.S, Pg/M, Zop.S /// - public static unsafe Vector Min(Vector left, Vector right) => Min(left, right); + public static unsafe Vector PopCount(Vector value) => PopCount(value); + /// + /// svuint32_t svcnt[_f32]_m(svuint32_t inactive, svbool_t pg, svfloat32_t op) + /// svuint32_t svcnt[_f32]_x(svbool_t pg, svfloat32_t op) + /// svuint32_t svcnt[_f32]_z(svbool_t pg, svfloat32_t op) + /// CNT Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector PopCount(Vector value) => PopCount(value); - /// MinAcross : Minimum reduction to scalar + /// + /// svuint32_t svcnt[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// svuint32_t svcnt[_u32]_x(svbool_t pg, svuint32_t op) + /// svuint32_t svcnt[_u32]_z(svbool_t pg, svuint32_t op) + /// CNT Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// uint8_t svminv[_u8](svbool_t pg, svuint8_t op) - /// UMINV Bresult, Pg, Zop.B + /// svuint64_t svcnt[_f64]_m(svuint64_t inactive, svbool_t pg, svfloat64_t op) + /// svuint64_t svcnt[_f64]_x(svbool_t pg, svfloat64_t op) + /// svuint64_t svcnt[_f64]_z(svbool_t pg, svfloat64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// float64_t svminv[_f64](svbool_t pg, svfloat64_t op) - /// FMINV Dresult, Pg, Zop.D + /// svuint64_t svcnt[_s64]_m(svuint64_t inactive, svbool_t pg, svint64_t op) + /// svuint64_t svcnt[_s64]_x(svbool_t pg, svint64_t op) + /// svuint64_t svcnt[_s64]_z(svbool_t pg, svint64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector PopCount(Vector value) => PopCount(value); /// - /// int16_t svminv[_s16](svbool_t pg, svint16_t op) - /// SMINV Hresult, Pg, Zop.H + /// svuint64_t svcnt[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// svuint64_t svcnt[_u64]_x(svbool_t pg, svuint64_t op) + /// svuint64_t svcnt[_u64]_z(svbool_t pg, svuint64_t op) + /// CNT Ztied.D, Pg/M, Zop.D /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector PopCount(Vector value) => PopCount(value); + + + /// Reverse all elements /// - /// int32_t svminv[_s32](svbool_t pg, svint32_t op) - /// SMINV Sresult, Pg, Zop.S + /// svuint8_t svrev[_u8](svuint8_t op) + /// REV Zresult.B, Zop.B /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// int64_t svminv[_s64](svbool_t pg, svint64_t op) - /// SMINV Dresult, Pg, Zop.D + /// svfloat64_t svrev[_f64](svfloat64_t op) + /// REV Zresult.D, Zop.D /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// int8_t svminv[_s8](svbool_t pg, svint8_t op) - /// SMINV Bresult, Pg, Zop.B + /// svint16_t svrev[_s16](svint16_t op) + /// REV Zresult.H, Zop.H /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// float32_t svminv[_f32](svbool_t pg, svfloat32_t op) - /// FMINV Sresult, Pg, Zop.S + /// svint32_t svrev[_s32](svint32_t op) + /// REV Zresult.S, Zop.S /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// uint16_t svminv[_u16](svbool_t pg, svuint16_t op) - /// UMINV Hresult, Pg, Zop.H + /// svint64_t svrev[_s64](svint64_t op) + /// REV Zresult.D, Zop.D /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// uint32_t svminv[_u32](svbool_t pg, svuint32_t op) - /// UMINV Sresult, Pg, Zop.S + /// svint8_t svrev[_s8](svint8_t op) + /// REV Zresult.B, Zop.B /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// uint64_t svminv[_u64](svbool_t pg, svuint64_t op) - /// UMINV Dresult, Pg, Zop.D + /// svfloat32_t svrev[_f32](svfloat32_t op) + /// REV Zresult.S, Zop.S /// - public static unsafe Vector MinAcross(Vector value) => MinAcross(value); + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); + /// + /// svuint16_t svrev[_u16](svuint16_t op) + /// REV Zresult.H, Zop.H + /// + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); - /// MinNumber : Minimum number + /// + /// svuint32_t svrev[_u32](svuint32_t op) + /// REV Zresult.S, Zop.S + /// + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); /// - /// svfloat64_t svminnm[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svminnm[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svminnm[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMINNM Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMINNM Ztied2.D, Pg/M, Ztied2.D, Zop1.D + /// svuint64_t svrev[_u64](svuint64_t op) + /// REV Zresult.D, Zop.D + /// + public static unsafe Vector ReverseElement(Vector value) => ReverseElement(value); + + + /// Reverse halfwords within elements + + /// + /// svint32_t svrevh[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// REVH Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement16(Vector value) => ReverseElement16(value); + + /// + /// svint64_t svrevh[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVH Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement16(Vector value) => ReverseElement16(value); + + /// + /// svuint32_t svrevh[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// REVH Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement16(Vector value) => ReverseElement16(value); + + /// + /// svuint64_t svrevh[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVH Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement16(Vector value) => ReverseElement16(value); + + + /// Reverse words within elements + + /// + /// svint64_t svrevw[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVW Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement32(Vector value) => ReverseElement32(value); + + /// + /// svuint64_t svrevw[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVW Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement32(Vector value) => ReverseElement32(value); + + + /// Reverse bytes within elements + + /// + /// svint16_t svrevb[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) + /// REVB Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + /// + /// svint32_t svrevb[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) + /// REVB Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + /// + /// svint64_t svrevb[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) + /// REVB Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + /// + /// svuint16_t svrevb[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) + /// REVB Ztied.H, Pg/M, Zop.H + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + /// + /// svuint32_t svrevb[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) + /// REVB Ztied.S, Pg/M, Zop.S + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + /// + /// svuint64_t svrevb[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) + /// REVB Ztied.D, Pg/M, Zop.D + /// + public static unsafe Vector ReverseElement8(Vector value) => ReverseElement8(value); + + + /// Saturating decrement by number of halfword elements + + /// + /// int32_t svqdech_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + /// + /// int64_t svqdech_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + /// + /// uint32_t svqdech_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + /// + /// uint64_t svqdech_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + /// + /// svint16_t svqdech_pat[_s16](svint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECH Ztied.H, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + /// + /// svuint16_t svqdech_pat[_u16](svuint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECH Ztied.H, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy16BitElementCount(value, scale, pattern); + + + /// Saturating decrement by number of word elements + + /// + /// int32_t svqdecw_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + /// + /// int64_t svqdecw_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + /// + /// uint32_t svqdecw_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + /// + /// uint64_t svqdecw_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + /// + /// svint32_t svqdecw_pat[_s32](svint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECW Ztied.S, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + /// + /// svuint32_t svqdecw_pat[_u32](svuint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECW Ztied.S, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy32BitElementCount(value, scale, pattern); + + + /// Saturating decrement by number of doubleword elements + + /// + /// int32_t svqdecd_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + /// + /// int64_t svqdecd_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + /// + /// uint32_t svqdecd_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Wtied, pattern, MUL #imm_factor + /// + public static unsafe uint SaturatingDecrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + /// + /// uint64_t svqdecd_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Xtied, pattern, MUL #imm_factor + /// + public static unsafe ulong SaturatingDecrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + /// + /// svint64_t svqdecd_pat[_s64](svint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECD Ztied.D, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + /// + /// svuint64_t svqdecd_pat[_u64](svuint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECD Ztied.D, pattern, MUL #imm_factor + /// + public static unsafe Vector SaturatingDecrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy64BitElementCount(value, scale, pattern); + + + /// Saturating decrement by number of byte elements + + /// + /// int32_t svqdecb_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECB Xtied, Wtied, pattern, MUL #imm_factor + /// + public static unsafe int SaturatingDecrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy8BitElementCount(value, scale, pattern); + + /// + /// int64_t svqdecb_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQDECB Xtied, pattern, MUL #imm_factor + /// + public static unsafe long SaturatingDecrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy8BitElementCount(value, scale, pattern); + + /// + /// uint32_t svqdecb_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECB Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MinNumber(Vector left, Vector right) => MinNumber(left, right); + public static unsafe uint SaturatingDecrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy8BitElementCount(value, scale, pattern); /// - /// svfloat32_t svminnm[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svminnm[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svminnm[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMINNM Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMINNM Ztied2.S, Pg/M, Ztied2.S, Zop1.S + /// uint64_t svqdecb_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQDECB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MinNumber(Vector left, Vector right) => MinNumber(left, right); + public static unsafe ulong SaturatingDecrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingDecrementBy8BitElementCount(value, scale, pattern); - /// MinNumberAcross : Minimum number reduction to scalar + /// Saturating decrement by active element count /// - /// float64_t svminnmv[_f64](svbool_t pg, svfloat64_t op) - /// FMINNMV Dresult, Pg, Zop.D + /// int32_t svqdecp[_n_s32]_b8(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.B, Wtied /// - public static unsafe Vector MinNumberAcross(Vector value) => MinNumberAcross(value); + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// float32_t svminnmv[_f32](svbool_t pg, svfloat32_t op) - /// FMINNMV Sresult, Pg, Zop.S + /// int64_t svqdecp[_n_s64]_b8(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.B /// - public static unsafe Vector MinNumberAcross(Vector value) => MinNumberAcross(value); + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); + /// + /// uint32_t svqdecp[_n_u32]_b8(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.B + /// + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint8_t svmul[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svint8_t svmul[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svint8_t svmul[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// uint64_t svqdecp[_n_u64]_b8(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.B /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint16_t svmul[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svint16_t svmul[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svint16_t svmul[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// svint16_t svqdecp[_s16](svint16_t op, svbool_t pg) + /// SQDECP Ztied.H, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint32_t svmul[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svint32_t svmul[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svint32_t svmul[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// svint32_t svqdecp[_s32](svint32_t op, svbool_t pg) + /// SQDECP Ztied.S, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint64_t svmul[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svint64_t svmul[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svint64_t svmul[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// svint64_t svqdecp[_s64](svint64_t op, svbool_t pg) + /// SQDECP Ztied.D, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint8_t svmul[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svuint8_t svmul[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MUL Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// MUL Ztied2.B, Pg/M, Ztied2.B, Zop1.B - /// MOVPRFX Zresult, Zop1; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// svuint8_t svmul[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// MOVPRFX Zresult.B, Pg/Z, Zop1.B; MUL Zresult.B, Pg/M, Zresult.B, Zop2.B - /// MOVPRFX Zresult.B, Pg/Z, Zop2.B; MUL Zresult.B, Pg/M, Zresult.B, Zop1.B + /// int32_t svqdecp[_n_s32]_b16(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.H, Wtied /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint16_t svmul[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svuint16_t svmul[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MUL Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// MUL Ztied2.H, Pg/M, Ztied2.H, Zop1.H - /// MOVPRFX Zresult, Zop1; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// svuint16_t svmul[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// MOVPRFX Zresult.H, Pg/Z, Zop1.H; MUL Zresult.H, Pg/M, Zresult.H, Zop2.H - /// MOVPRFX Zresult.H, Pg/Z, Zop2.H; MUL Zresult.H, Pg/M, Zresult.H, Zop1.H + /// int64_t svqdecp[_n_s64]_b16(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.H /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint32_t svmul[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svuint32_t svmul[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// MOVPRFX Zresult, Zop1; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svuint32_t svmul[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; MUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; MUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// uint32_t svqdecp[_n_u32]_b16(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.H /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint64_t svmul[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svuint64_t svmul[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// MOVPRFX Zresult, Zop1; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svuint64_t svmul[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; MUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; MUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// uint64_t svqdecp[_n_u64]_b16(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.H /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svfloat32_t svmul[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svfloat32_t svmul[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMUL Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// FMUL Ztied2.S, Pg/M, Ztied2.S, Zop1.S - /// FMUL Zresult.S, Zop1.S, Zop2.S - /// MOVPRFX Zresult, Zop1; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// svfloat32_t svmul[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// MOVPRFX Zresult.S, Pg/Z, Zop1.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop2.S - /// MOVPRFX Zresult.S, Pg/Z, Zop2.S; FMUL Zresult.S, Pg/M, Zresult.S, Zop1.S + /// svuint16_t svqdecp[_u16](svuint16_t op, svbool_t pg) + /// UQDECP Ztied.H, Pg /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svfloat64_t svmul[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svfloat64_t svmul[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMUL Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// FMUL Ztied2.D, Pg/M, Ztied2.D, Zop1.D - /// FMUL Zresult.D, Zop1.D, Zop2.D - /// MOVPRFX Zresult, Zop1; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// svfloat64_t svmul[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// MOVPRFX Zresult.D, Pg/Z, Zop1.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop2.D - /// MOVPRFX Zresult.D, Pg/Z, Zop2.D; FMUL Zresult.D, Pg/M, Zresult.D, Zop1.D + /// int32_t svqdecp[_n_s32]_b32(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.S, Wtied /// - public static unsafe Vector Multiply(Vector left, Vector right) => Multiply(left, right); + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); - /// MultiplyAdd : Multiply-add, addend first + /// + /// int64_t svqdecp[_n_s64]_b32(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.S + /// + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint8_t svmla[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmla[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmla[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// uint32_t svqdecp[_n_u32]_b32(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.S /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint16_t svmla[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmla[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmla[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// uint64_t svqdecp[_n_u64]_b32(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.S /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint32_t svmla[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmla[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmla[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// svuint32_t svqdecp[_u32](svuint32_t op, svbool_t pg) + /// UQDECP Ztied.S, Pg /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint64_t svmla[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmla[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmla[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// int32_t svqdecp[_n_s32]_b64(int32_t op, svbool_t pg) + /// SQDECP Xtied, Pg.D, Wtied /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe long SaturatingDecrementByActiveElementCount(int value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svint8_t svmla[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmla[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmla[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// MLA Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// int64_t svqdecp[_n_s64]_b64(int64_t op, svbool_t pg) + /// SQDECP Xtied, Pg.D /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe long SaturatingDecrementByActiveElementCount(long value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint16_t svmla[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmla[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmla[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// MLA Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// uint32_t svqdecp[_n_u32]_b64(uint32_t op, svbool_t pg) + /// UQDECP Wtied, Pg.D /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(uint value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint32_t svmla[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmla[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmla[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// MLA Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// uint64_t svqdecp[_n_u64]_b64(uint64_t op, svbool_t pg) + /// UQDECP Xtied, Pg.D /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe ulong SaturatingDecrementByActiveElementCount(ulong value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); /// - /// svuint64_t svmla[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmla[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmla[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// MLA Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// svuint64_t svqdecp[_u64](svuint64_t op, svbool_t pg) + /// UQDECP Ztied.D, Pg /// - public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + public static unsafe Vector SaturatingDecrementByActiveElementCount(Vector value, Vector from) => SaturatingDecrementByActiveElementCount(value, from); - /// MultiplyBySelectedScalar : Multiply + + /// Saturating increment by number of halfword elements /// - /// svfloat64_t svmul_lane[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm_index) - /// FMUL Zresult.D, Zop1.D, Zop2.D[imm_index] + /// int32_t svqinch_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) => MultiplyBySelectedScalar(left, right, rightIndex); + public static unsafe int SaturatingIncrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); /// - /// svfloat32_t svmul_lane[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm_index) - /// FMUL Zresult.S, Zop1.S, Zop2.S[imm_index] + /// int64_t svqinch_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplyBySelectedScalar(Vector left, Vector right, [ConstantExpected] byte rightIndex) => MultiplyBySelectedScalar(left, right, rightIndex); - - /// MultiplyExtended : Multiply extended (∞×0=2) + public static unsafe long SaturatingIncrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); /// - /// svfloat64_t svmulx[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmulx[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// svfloat64_t svmulx[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2) - /// FMULX Ztied1.D, Pg/M, Ztied1.D, Zop2.D + /// uint32_t svqinch_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplyExtended(Vector left, Vector right) => MultiplyExtended(left, right); + public static unsafe uint SaturatingIncrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); /// - /// svfloat32_t svmulx[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmulx[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// svfloat32_t svmulx[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2) - /// FMULX Ztied1.S, Pg/M, Ztied1.S, Zop2.S + /// uint64_t svqinch_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplyExtended(Vector left, Vector right) => MultiplyExtended(left, right); - - /// MultiplySubtract : Multiply-subtract, minuend first + public static unsafe ulong SaturatingIncrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); /// - /// svuint8_t svmls[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmls[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// svuint8_t svmls[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2, svuint8_t op3) - /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// svint16_t svqinch_pat[_s16](svint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCH Ztied.H, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe Vector SaturatingIncrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); /// - /// svint16_t svmls[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmls[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// svint16_t svmls[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2, svint16_t op3) - /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// svuint16_t svqinch_pat[_u16](svuint16_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCH Ztied.H, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe Vector SaturatingIncrementBy16BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy16BitElementCount(value, scale, pattern); + + + /// Saturating increment by number of word elements /// - /// svint32_t svmls[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmls[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// svint32_t svmls[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2, svint32_t op3) - /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// int32_t svqincw_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe int SaturatingIncrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); /// - /// svint64_t svmls[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmls[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// svint64_t svmls[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2, svint64_t op3) - /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// int64_t svqincw_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe long SaturatingIncrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); /// - /// svint8_t svmls[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmls[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// svint8_t svmls[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2, svint8_t op3) - /// MLS Ztied1.B, Pg/M, Zop2.B, Zop3.B + /// uint32_t svqincw_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe uint SaturatingIncrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); /// - /// svuint16_t svmls[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmls[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// svuint16_t svmls[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2, svuint16_t op3) - /// MLS Ztied1.H, Pg/M, Zop2.H, Zop3.H + /// uint64_t svqincw_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe ulong SaturatingIncrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); /// - /// svuint32_t svmls[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmls[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// svuint32_t svmls[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2, svuint32_t op3) - /// MLS Ztied1.S, Pg/M, Zop2.S, Zop3.S + /// svint32_t svqincw_pat[_s32](svint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCW Ztied.S, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe Vector SaturatingIncrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); /// - /// svuint64_t svmls[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmls[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// svuint64_t svmls[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2, svuint64_t op3) - /// MLS Ztied1.D, Pg/M, Zop2.D, Zop3.D + /// svuint32_t svqincw_pat[_u32](svuint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCW Ztied.S, pattern, MUL #imm_factor /// - public static unsafe Vector MultiplySubtract(Vector minuend, Vector left, Vector right) => MultiplySubtract(minuend, left, right); + public static unsafe Vector SaturatingIncrementBy32BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy32BitElementCount(value, scale, pattern); - /// Negate : Negate + + /// Saturating increment by number of doubleword elements /// - /// svfloat64_t svneg[_f64]_m(svfloat64_t inactive, svbool_t pg, svfloat64_t op) - /// svfloat64_t svneg[_f64]_x(svbool_t pg, svfloat64_t op) - /// svfloat64_t svneg[_f64]_z(svbool_t pg, svfloat64_t op) - /// FNEG Ztied.D, Pg/M, Zop.D + /// int32_t svqincd_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe int SaturatingIncrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); /// - /// svint16_t svneg[_s16]_m(svint16_t inactive, svbool_t pg, svint16_t op) - /// svint16_t svneg[_s16]_x(svbool_t pg, svint16_t op) - /// svint16_t svneg[_s16]_z(svbool_t pg, svint16_t op) - /// NEG Ztied.H, Pg/M, Zop.H + /// int64_t svqincd_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe long SaturatingIncrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); /// - /// svint32_t svneg[_s32]_m(svint32_t inactive, svbool_t pg, svint32_t op) - /// svint32_t svneg[_s32]_x(svbool_t pg, svint32_t op) - /// svint32_t svneg[_s32]_z(svbool_t pg, svint32_t op) - /// NEG Ztied.S, Pg/M, Zop.S + /// uint32_t svqincd_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe uint SaturatingIncrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); /// - /// svint64_t svneg[_s64]_m(svint64_t inactive, svbool_t pg, svint64_t op) - /// svint64_t svneg[_s64]_x(svbool_t pg, svint64_t op) - /// svint64_t svneg[_s64]_z(svbool_t pg, svint64_t op) - /// NEG Ztied.D, Pg/M, Zop.D + /// uint64_t svqincd_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe ulong SaturatingIncrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); /// - /// svint8_t svneg[_s8]_x(svbool_t pg, svint8_t op) - /// svint8_t svneg[_s8]_m(svint8_t inactive, svbool_t pg, svint8_t op) - /// svint8_t svneg[_s8]_z(svbool_t pg, svint8_t op) - /// NEG Ztied.B, Pg/M, Zop.B + /// svint64_t svqincd_pat[_s64](svint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCD Ztied.D, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe Vector SaturatingIncrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); /// - /// svfloat32_t svneg[_f32]_m(svfloat32_t inactive, svbool_t pg, svfloat32_t op) - /// svfloat32_t svneg[_f32]_x(svbool_t pg, svfloat32_t op) - /// svfloat32_t svneg[_f32]_z(svbool_t pg, svfloat32_t op) - /// FNEG Ztied.S, Pg/M, Zop.S + /// svuint64_t svqincd_pat[_u64](svuint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCD Ztied.D, pattern, MUL #imm_factor /// - public static unsafe Vector Negate(Vector value) => Negate(value); + public static unsafe Vector SaturatingIncrementBy64BitElementCount(Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy64BitElementCount(value, scale, pattern); - /// Or : Bitwise inclusive OR + + /// Saturating increment by number of byte elements /// - /// svuint8_t svorr[_u8]_m(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svorr[_u8]_x(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// svuint8_t svorr[_u8]_z(svbool_t pg, svuint8_t op1, svuint8_t op2) - /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int32_t svqincb_pat[_n_s32](int32_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCB Xtied, Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe int SaturatingIncrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy8BitElementCount(value, scale, pattern); /// - /// svint16_t svorr[_s16]_m(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svorr[_s16]_x(svbool_t pg, svint16_t op1, svint16_t op2) - /// svint16_t svorr[_s16]_z(svbool_t pg, svint16_t op1, svint16_t op2) - /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int64_t svqincb_pat[_n_s64](int64_t op, enum svpattern pattern, uint64_t imm_factor) + /// SQINCB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe long SaturatingIncrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy8BitElementCount(value, scale, pattern); /// - /// svint32_t svorr[_s32]_m(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svorr[_s32]_x(svbool_t pg, svint32_t op1, svint32_t op2) - /// svint32_t svorr[_s32]_z(svbool_t pg, svint32_t op1, svint32_t op2) - /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint32_t svqincb_pat[_n_u32](uint32_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCB Wtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe uint SaturatingIncrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy8BitElementCount(value, scale, pattern); /// - /// svint64_t svorr[_s64]_m(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svorr[_s64]_x(svbool_t pg, svint64_t op1, svint64_t op2) - /// svint64_t svorr[_s64]_z(svbool_t pg, svint64_t op1, svint64_t op2) - /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint64_t svqincb_pat[_n_u64](uint64_t op, enum svpattern pattern, uint64_t imm_factor) + /// UQINCB Xtied, pattern, MUL #imm_factor /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe ulong SaturatingIncrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) => SaturatingIncrementBy8BitElementCount(value, scale, pattern); + + + /// Saturating increment by active element count /// - /// svint8_t svorr[_s8]_m(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svorr[_s8]_x(svbool_t pg, svint8_t op1, svint8_t op2) - /// svint8_t svorr[_s8]_z(svbool_t pg, svint8_t op1, svint8_t op2) - /// ORR Ztied1.B, Pg/M, Ztied1.B, Zop2.B - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int32_t svqincp[_n_s32]_b8(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.B, Wtied /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint16_t svorr[_u16]_m(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svorr[_u16]_x(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// svuint16_t svorr[_u16]_z(svbool_t pg, svuint16_t op1, svuint16_t op2) - /// ORR Ztied1.H, Pg/M, Ztied1.H, Zop2.H - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// int64_t svqincp[_n_s64]_b8(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.B /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint32_t svorr[_u32]_m(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svorr[_u32]_x(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// svuint32_t svorr[_u32]_z(svbool_t pg, svuint32_t op1, svuint32_t op2) - /// ORR Ztied1.S, Pg/M, Ztied1.S, Zop2.S - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint32_t svqincp[_n_u32]_b8(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.B /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint64_t svorr[_u64]_m(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svorr[_u64]_x(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// svuint64_t svorr[_u64]_z(svbool_t pg, svuint64_t op1, svuint64_t op2) - /// ORR Ztied1.D, Pg/M, Ztied1.D, Zop2.D - /// ORR Zresult.D, Zop1.D, Zop2.D - /// svbool_t svorr[_b]_z(svbool_t pg, svbool_t op1, svbool_t op2) - /// ORR Presult.B, Pg/Z, Pop1.B, Pop2.B + /// uint64_t svqincp[_n_u64]_b8(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.B /// - public static unsafe Vector Or(Vector left, Vector right) => Or(left, right); - - - /// OrAcross : Bitwise inclusive OR reduction to scalar + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// uint8_t svorv[_u8](svbool_t pg, svuint8_t op) - /// ORV Bresult, Pg, Zop.B + /// svint16_t svqincp[_s16](svint16_t op, svbool_t pg) + /// SQINCP Ztied.H, Pg /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// int16_t svorv[_s16](svbool_t pg, svint16_t op) - /// ORV Hresult, Pg, Zop.H + /// svint32_t svqincp[_s32](svint32_t op, svbool_t pg) + /// SQINCP Ztied.S, Pg /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// int32_t svorv[_s32](svbool_t pg, svint32_t op) - /// ORV Sresult, Pg, Zop.S + /// svint64_t svqincp[_s64](svint64_t op, svbool_t pg) + /// SQINCP Ztied.D, Pg /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// int64_t svorv[_s64](svbool_t pg, svint64_t op) - /// ORV Dresult, Pg, Zop.D + /// int32_t svqincp[_n_s32]_b16(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.H, Wtied /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// int8_t svorv[_s8](svbool_t pg, svint8_t op) - /// ORV Bresult, Pg, Zop.B + /// int64_t svqincp[_n_s64]_b16(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.H /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// uint16_t svorv[_u16](svbool_t pg, svuint16_t op) - /// ORV Hresult, Pg, Zop.H + /// uint32_t svqincp[_n_u32]_b16(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.H /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// uint32_t svorv[_u32](svbool_t pg, svuint32_t op) - /// ORV Sresult, Pg, Zop.S + /// uint64_t svqincp[_n_u64]_b16(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.H /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// uint64_t svorv[_u64](svbool_t pg, svuint64_t op) - /// ORV Dresult, Pg, Zop.D + /// svuint16_t svqincp[_u16](svuint16_t op, svbool_t pg) + /// UQINCP Ztied.H, Pg /// - public static unsafe Vector OrAcross(Vector value) => OrAcross(value); - - - /// Count nonzero bits + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint8_t svcnt[_s8]_m(svuint8_t inactive, svbool_t pg, svint8_t op) - /// svuint8_t svcnt[_s8]_x(svbool_t pg, svint8_t op) - /// svuint8_t svcnt[_s8]_z(svbool_t pg, svint8_t op) - /// CNT Ztied.B, Pg/M, Zop.B + /// int32_t svqincp[_n_s32]_b32(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.S, Wtied /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint8_t svcnt[_u8]_m(svuint8_t inactive, svbool_t pg, svuint8_t op) - /// svuint8_t svcnt[_u8]_x(svbool_t pg, svuint8_t op) - /// svuint8_t svcnt[_u8]_z(svbool_t pg, svuint8_t op) - /// CNT Ztied.B, Pg/M, Zop.B + /// int64_t svqincp[_n_s64]_b32(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint16_t svcnt[_s16]_m(svuint16_t inactive, svbool_t pg, svint16_t op) - /// svuint16_t svcnt[_s16]_x(svbool_t pg, svint16_t op) - /// svuint16_t svcnt[_s16]_z(svbool_t pg, svint16_t op) - /// CNT Ztied.H, Pg/M, Zop.H + /// uint32_t svqincp[_n_u32]_b32(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint16_t svcnt[_u16]_m(svuint16_t inactive, svbool_t pg, svuint16_t op) - /// svuint16_t svcnt[_u16]_x(svbool_t pg, svuint16_t op) - /// svuint16_t svcnt[_u16]_z(svbool_t pg, svuint16_t op) - /// CNT Ztied.H, Pg/M, Zop.H + /// uint64_t svqincp[_n_u64]_b32(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.S /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint32_t svcnt[_s32]_m(svuint32_t inactive, svbool_t pg, svint32_t op) - /// svuint32_t svcnt[_s32]_x(svbool_t pg, svint32_t op) - /// svuint32_t svcnt[_s32]_z(svbool_t pg, svint32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// svuint32_t svqincp[_u32](svuint32_t op, svbool_t pg) + /// UQINCP Ztied.S, Pg /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint32_t svcnt[_f32]_m(svuint32_t inactive, svbool_t pg, svfloat32_t op) - /// svuint32_t svcnt[_f32]_x(svbool_t pg, svfloat32_t op) - /// svuint32_t svcnt[_f32]_z(svbool_t pg, svfloat32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// int32_t svqincp[_n_s32]_b64(int32_t op, svbool_t pg) + /// SQINCP Xtied, Pg.D, Wtied /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe long SaturatingIncrementByActiveElementCount(int value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint32_t svcnt[_u32]_m(svuint32_t inactive, svbool_t pg, svuint32_t op) - /// svuint32_t svcnt[_u32]_x(svbool_t pg, svuint32_t op) - /// svuint32_t svcnt[_u32]_z(svbool_t pg, svuint32_t op) - /// CNT Ztied.S, Pg/M, Zop.S + /// int64_t svqincp[_n_s64]_b64(int64_t op, svbool_t pg) + /// SQINCP Xtied, Pg.D /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe long SaturatingIncrementByActiveElementCount(long value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint64_t svcnt[_f64]_m(svuint64_t inactive, svbool_t pg, svfloat64_t op) - /// svuint64_t svcnt[_f64]_x(svbool_t pg, svfloat64_t op) - /// svuint64_t svcnt[_f64]_z(svbool_t pg, svfloat64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// uint32_t svqincp[_n_u32]_b64(uint32_t op, svbool_t pg) + /// UQINCP Wtied, Pg.D /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(uint value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint64_t svcnt[_s64]_m(svuint64_t inactive, svbool_t pg, svint64_t op) - /// svuint64_t svcnt[_s64]_x(svbool_t pg, svint64_t op) - /// svuint64_t svcnt[_s64]_z(svbool_t pg, svint64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// uint64_t svqincp[_n_u64]_b64(uint64_t op, svbool_t pg) + /// UQINCP Xtied, Pg.D /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe ulong SaturatingIncrementByActiveElementCount(ulong value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// - /// svuint64_t svcnt[_u64]_m(svuint64_t inactive, svbool_t pg, svuint64_t op) - /// svuint64_t svcnt[_u64]_x(svbool_t pg, svuint64_t op) - /// svuint64_t svcnt[_u64]_z(svbool_t pg, svuint64_t op) - /// CNT Ztied.D, Pg/M, Zop.D + /// svuint64_t svqincp[_u64](svuint64_t op, svbool_t pg) + /// UQINCP Ztied.D, Pg /// - public static unsafe Vector PopCount(Vector value) => PopCount(value); + public static unsafe Vector SaturatingIncrementByActiveElementCount(Vector value, Vector from) => SaturatingIncrementByActiveElementCount(value, from); /// SignExtend16 : Sign-extend the low 16 bits @@ -3170,6 +4436,69 @@ internal Arm64() { } public static unsafe void StoreNarrowing(Vector mask, uint* address, Vector data) => StoreNarrowing(mask, address, data); + /// StoreNonTemporal : Non-truncating store, non-temporal + + /// + /// void svstnt1[_u8](svbool_t pg, uint8_t *base, svuint8_t data) + /// STNT1B Zdata.B, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, byte* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_f64](svbool_t pg, float64_t *base, svfloat64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, double* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_s16](svbool_t pg, int16_t *base, svint16_t data) + /// STNT1H Zdata.H, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, short* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_s32](svbool_t pg, int32_t *base, svint32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, int* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_s64](svbool_t pg, int64_t *base, svint64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, long* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_s8](svbool_t pg, int8_t *base, svint8_t data) + /// STNT1B Zdata.B, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, sbyte* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_f32](svbool_t pg, float32_t *base, svfloat32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, float* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_u16](svbool_t pg, uint16_t *base, svuint16_t data) + /// STNT1H Zdata.H, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, ushort* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_u32](svbool_t pg, uint32_t *base, svuint32_t data) + /// STNT1W Zdata.S, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, uint* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// + /// void svstnt1[_u64](svbool_t pg, uint64_t *base, svuint64_t data) + /// STNT1D Zdata.D, Pg, [Xbase, #0, MUL VL] + /// + public static unsafe void StoreNonTemporal(Vector mask, ulong* address, Vector data) => StoreNonTemporal(mask, address, data); + + /// Subtract : Subtract /// @@ -3345,6 +4674,132 @@ internal Arm64() { } public static unsafe Vector SubtractSaturate(Vector left, Vector right) => SubtractSaturate(left, right); + /// Interleave even elements from two inputs + + /// + /// svuint8_t svtrn1[_u8](svuint8_t op1, svuint8_t op2) + /// TRN1 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svfloat64_t svtrn1[_f64](svfloat64_t op1, svfloat64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svint16_t svtrn1[_s16](svint16_t op1, svint16_t op2) + /// TRN1 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svint32_t svtrn1[_s32](svint32_t op1, svint32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svint64_t svtrn1[_s64](svint64_t op1, svint64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svint8_t svtrn1[_s8](svint8_t op1, svint8_t op2) + /// TRN1 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svfloat32_t svtrn1[_f32](svfloat32_t op1, svfloat32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svuint16_t svtrn1[_u16](svuint16_t op1, svuint16_t op2) + /// TRN1 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svuint32_t svtrn1[_u32](svuint32_t op1, svuint32_t op2) + /// TRN1 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + /// + /// svuint64_t svtrn1[_u64](svuint64_t op1, svuint64_t op2) + /// TRN1 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeEven(Vector left, Vector right) => TransposeEven(left, right); + + + /// Interleave odd elements from two inputs + + /// + /// svuint8_t svtrn2[_u8](svuint8_t op1, svuint8_t op2) + /// TRN2 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svfloat64_t svtrn2[_f64](svfloat64_t op1, svfloat64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svint16_t svtrn2[_s16](svint16_t op1, svint16_t op2) + /// TRN2 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svint32_t svtrn2[_s32](svint32_t op1, svint32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svint64_t svtrn2[_s64](svint64_t op1, svint64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svint8_t svtrn2[_s8](svint8_t op1, svint8_t op2) + /// TRN2 Zresult.B, Zop1.B, Zop2.B + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svfloat32_t svtrn2[_f32](svfloat32_t op1, svfloat32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svuint16_t svtrn2[_u16](svuint16_t op1, svuint16_t op2) + /// TRN2 Zresult.H, Zop1.H, Zop2.H + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svuint32_t svtrn2[_u32](svuint32_t op1, svuint32_t op2) + /// TRN2 Zresult.S, Zop1.S, Zop2.S + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// + /// svuint64_t svtrn2[_u64](svuint64_t op1, svuint64_t op2) + /// TRN2 Zresult.D, Zop1.D, Zop2.D + /// + public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + + /// UnzipEven : Concatenate even elements from two inputs /// @@ -3890,5 +5345,6 @@ internal Arm64() { } /// ZIP1 Presult.D, Pop1.D, Pop2.D /// public static unsafe Vector ZipLow(Vector left, Vector right) => ZipLow(left, right); + } } 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 1cb989eedba4d..7f83b8c09d79e 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 @@ -71,7 +71,6 @@ public static Vector128 Abs(Vector128 vector) /// The sum of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Add(Vector128 left, Vector128 right) => left + right; /// Computes the bitwise-and of a given vector and the ones complement of another vector. @@ -102,7 +101,11 @@ public static Vector128 As(this Vector128 vector) ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); +#if MONO return Unsafe.As, Vector128>(ref vector); +#else + return Unsafe.BitCast, Vector128>(vector); +#endif } /// Reinterprets a as a new . @@ -111,7 +114,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsByte(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -120,7 +122,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsDouble(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -129,7 +130,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsInt16(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -138,7 +138,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsInt32(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -147,7 +146,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsInt64(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -156,7 +154,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsNInt(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -166,9 +163,32 @@ public static Vector128 As(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsNUInt(this Vector128 vector) => vector.As(); + /// Reinterprets a as a new . + /// The vector to reinterpret. + /// reinterpreted as a new . + internal static Plane AsPlane(this Vector128 value) + { +#if MONO + return Unsafe.As, Plane>(ref value); +#else + return Unsafe.BitCast, Plane>(value); +#endif + } + + /// Reinterprets a as a new . + /// The vector to reinterpret. + /// reinterpreted as a new . + internal static Quaternion AsQuaternion(this Vector128 value) + { +#if MONO + return Unsafe.As, Quaternion>(ref value); +#else + return Unsafe.BitCast, Quaternion>(value); +#endif + } + /// Reinterprets a as a new . /// The type of the elements in the vector. /// The vector to reinterpret. @@ -176,7 +196,6 @@ public static Vector128 As(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsSByte(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -185,7 +204,6 @@ public static Vector128 As(this Vector128 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsSingle(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -195,7 +213,6 @@ public static Vector128 As(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsUInt16(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -205,7 +222,6 @@ public static Vector128 As(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsUInt32(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . @@ -215,48 +231,58 @@ public static Vector128 As(this Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsUInt64(this Vector128 vector) => vector.As(); /// Reinterprets a as a new . /// The plane to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector128 AsVector128(this Plane value) - => Unsafe.As>(ref value); + { +#if MONO + return Unsafe.As>(ref value); +#else + return Unsafe.BitCast>(value); +#endif + } /// Reinterprets a as a new . /// The quaternion to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector128 AsVector128(this Quaternion value) - => Unsafe.As>(ref value); + { +#if MONO + return Unsafe.As>(ref value); +#else + return Unsafe.BitCast>(value); +#endif + } /// Reinterprets a as a new . /// The vector to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 AsVector128(this Vector2 value) - => new Vector4(value, 0.0f, 0.0f).AsVector128(); + public static Vector128 AsVector128(this Vector2 value) => new Vector4(value.X, value.Y, 0.0f, 0.0f).AsVector128(); /// Reinterprets a as a new . /// The vector to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 AsVector128(this Vector3 value) - => new Vector4(value, 0.0f).AsVector128(); + public static Vector128 AsVector128(this Vector3 value) => new Vector4(value.X, value.Y, value.Z, 0.0f).AsVector128(); /// Reinterprets a as a new . /// The vector to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 AsVector128(this Vector4 value) - => Unsafe.As>(ref value); + { +#if MONO + return Unsafe.As>(ref value); +#else + return Unsafe.BitCast>(value); +#endif + } /// Reinterprets a as a new . /// The type of the elements in the vector. @@ -300,9 +326,14 @@ public static Vector3 AsVector3(this Vector128 value) /// The vector to reinterpret. /// reinterpreted as a new . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AsVector4(this Vector128 value) - => Unsafe.As, Vector4>(ref value); + { +#if MONO + return Unsafe.As, Vector4>(ref value); +#else + return Unsafe.BitCast, Vector4>(value); +#endif + } /// Reinterprets a as a new . /// The type of the elements in the vector. @@ -328,7 +359,6 @@ public static Vector AsVector(this Vector128 value) /// The bitwise-and of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 BitwiseAnd(Vector128 left, Vector128 right) => left & right; /// Computes the bitwise-or of two vectors. @@ -338,7 +368,6 @@ public static Vector AsVector(this Vector128 value) /// The bitwise-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 BitwiseOr(Vector128 left, Vector128 right) => left | right; /// Computes the ceiling of each element in a vector. @@ -359,7 +388,6 @@ internal static Vector128 Ceiling(Vector128 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Ceiling(Vector128 vector) => Ceiling(vector); /// Computes the ceiling of each element in a vector. @@ -367,7 +395,6 @@ internal static Vector128 Ceiling(Vector128 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Ceiling(Vector128 vector) => Ceiling(vector); /// Conditionally selects a value from two vectors on a bitwise basis. @@ -716,7 +743,6 @@ public static void CopyTo(this Vector128 vector, Span destination) /// A new with all elements initialized to . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(T value) { Vector64 vector = Vector64.Create(value); @@ -728,7 +754,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi8 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -736,7 +761,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128d _mm_set1_pd [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -744,7 +768,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi16 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -752,7 +775,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi32 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -760,14 +782,12 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128i _mm_set1_epi64x [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -775,7 +795,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -784,7 +803,6 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi8 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -792,7 +810,6 @@ public static unsafe Vector128 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m128 _mm_set1_ps [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -801,7 +818,6 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi16 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -810,7 +826,6 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi32 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -819,7 +834,6 @@ public static unsafe Vector128 Create(T value) /// On x86, this method corresponds to __m128i _mm_set1_epi64x [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(ulong value) => Create(value); /// Creates a new from a given array. @@ -1109,21 +1123,18 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1131,21 +1142,18 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the upper 64-bits will be initialized to. /// On x86, this method corresponds to __m128i _mm_setr_epi64 /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1153,7 +1161,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1161,14 +1168,12 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 64-bits will be initialized to. /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1176,7 +1181,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1185,7 +1189,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// On x86, this method corresponds to __m128i _mm_setr_epi64 /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1193,7 +1196,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// The value that the upper 64-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Create(Vector64 lower, Vector64 upper) => Create(lower, upper); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1202,49 +1204,42 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(T value) => Vector64.CreateScalar(value).ToVector128(); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1252,7 +1247,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1260,14 +1254,12 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1275,7 +1267,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1283,7 +1274,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1291,7 +1281,6 @@ public static Vector128 Create(Vector64 lower, Vector64 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1317,42 +1306,36 @@ public static Vector128 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1360,7 +1343,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1368,14 +1350,12 @@ public static Vector128 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1383,7 +1363,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1391,7 +1370,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1399,7 +1377,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. @@ -1418,7 +1395,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// The quotient of divided by . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Divide(Vector128 left, Vector128 right) => left / right; /// Divides a vector by a scalar to compute the per-element quotient. @@ -1427,7 +1403,6 @@ public static Vector128 CreateScalarUnsafe(T value) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Divide(Vector128 left, T right) => left / right; /// Computes the dot product of two vectors. @@ -1472,7 +1447,6 @@ public static Vector128 Equals(Vector128 left, Vector128 right) /// true if all elements in were equal to the corresponding element in . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsAll(Vector128 left, Vector128 right) => left == right; /// Compares two vectors to determine if any elements are equal. @@ -1556,7 +1530,6 @@ internal static Vector128 Floor(Vector128 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Floor(Vector128 vector) => Floor(vector); /// Computes the floor of each element in a vector. @@ -1564,7 +1537,6 @@ internal static Vector128 Floor(Vector128 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Floor(Vector128 vector) => Floor(vector); /// @@ -1616,7 +1588,6 @@ public static T GetElement(this Vector128 vector, int index) /// The value of the lower 64-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 GetLower(this Vector128 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -1629,7 +1600,6 @@ public static Vector64 GetLower(this Vector128 vector) /// The value of the upper 64-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 GetUpper(this Vector128 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -1820,7 +1790,6 @@ public static bool LessThanOrEqualAny(Vector128 left, Vector128 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. @@ -1851,7 +1820,6 @@ public static unsafe Vector128 LoadAligned(T* source) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector128 LoadAlignedNonTemporal(T* source) => LoadAligned(source); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1888,17 +1856,13 @@ public static Vector128 LoadUnsafe(ref readonly T source, nuint elementOff /// Loads a vector from the given source and reinterprets it as . /// The source from which the vector will be loaded. /// The vector loaded from . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector128 LoadUnsafe(ref char source) => - LoadUnsafe(ref Unsafe.As(ref source)); + internal static Vector128 LoadUnsafe(ref char source) => LoadUnsafe(ref Unsafe.As(ref source)); /// Loads a vector from the given source and element offset and reinterprets it as . /// The source to which will be added before loading the vector. /// The element offset from from which the vector will be loaded. /// The vector loaded from plus . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector128 LoadUnsafe(ref char source, nuint elementOffset) => - LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + internal static Vector128 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2007,7 +1971,6 @@ public static Vector128 Min(Vector128 left, Vector128 right) /// The element-wise product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Multiply(Vector128 left, Vector128 right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -2017,7 +1980,6 @@ public static Vector128 Min(Vector128 left, Vector128 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Multiply(Vector128 left, T right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -2027,7 +1989,6 @@ public static Vector128 Min(Vector128 left, Vector128 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Multiply(T left, Vector128 right) => left * right; /// @@ -2160,7 +2121,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
    A vector whose elements are the negation of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Negate(Vector128 vector) => -vector; /// Computes the ones-complement of a vector. @@ -2169,7 +2129,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
      A vector whose elements are the ones-complement of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 OnesComplement(Vector128 vector) => ~vector; /// Shifts each element of a vector left by the specified amount. @@ -2177,7 +2136,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
        The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2185,7 +2143,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
          The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2193,7 +2150,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
            The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2201,7 +2157,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
              The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2209,7 +2164,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2217,7 +2171,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                  The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2226,7 +2179,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                    A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2235,7 +2187,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                      A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2244,7 +2195,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                        A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2253,7 +2203,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                          A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2262,7 +2211,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                            A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftLeft(Vector128 vector, int shiftCount) => vector << shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2270,7 +2218,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                              The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2278,7 +2225,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2286,7 +2232,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                  The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2294,7 +2239,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                    The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2302,7 +2246,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                      The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2311,7 +2254,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                        A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightArithmetic(Vector128 vector, int shiftCount) => vector >> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2319,7 +2261,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                          The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2327,7 +2268,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                            The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2335,7 +2275,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                              The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2343,7 +2282,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2351,7 +2289,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                  The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2359,7 +2296,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                    The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2368,7 +2304,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                      A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2377,7 +2312,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                        A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2386,7 +2320,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                          A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2395,7 +2328,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                            A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2404,7 +2336,6 @@ public static unsafe Vector128 Narrow(Vector128 lower, Vector128
                                                              A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ShiftRightLogical(Vector128 vector, int shiftCount) => vector >>> shiftCount; /// Creates a new vector by selecting values from an input vector using a set of indices. @@ -2706,7 +2637,6 @@ public static Vector128 Sqrt(Vector128 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Store(this Vector128 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. @@ -2737,7 +2667,6 @@ public static unsafe void StoreAligned(this Vector128 source, T* destinati /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void StoreAlignedNonTemporal(this Vector128 source, T* destination) => source.StoreAligned(destination); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -2795,7 +2724,6 @@ public static void StoreUnsafe(this Vector128 source, ref T destination, n /// The difference of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Subtract(Vector128 left, Vector128 right) => left - right; /// Computes the sum of all elements in a vector. @@ -2823,7 +2751,6 @@ public static T Sum(Vector128 vector) /// A scalar containing the value of the first element. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ToScalar(this Vector128 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -3210,7 +3137,6 @@ public static Vector128 WithUpper(this Vector128 vector, Vector64 va /// The exclusive-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Xor(Vector128 left, Vector128 right) => left ^ right; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -3230,16 +3156,10 @@ internal static void SetElementUnsafe(in this Vector128 vector, int index, } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetLowerUnsafe(in this Vector128 vector, Vector64 value) - { - Unsafe.AsRef(in vector._lower) = value; - } + internal static void SetLowerUnsafe(in this Vector128 vector, Vector64 value) => Unsafe.AsRef(in vector._lower) = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetUpperUnsafe(in this Vector128 vector, Vector64 value) - { - Unsafe.AsRef(in vector._upper) = value; - } + internal static void SetUpperUnsafe(in this Vector128 vector, Vector64 value) => Unsafe.AsRef(in vector._upper) = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs index 23f027f12f718..c9f1b6e3f21ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs @@ -39,7 +39,6 @@ namespace System.Runtime.Intrinsics public static Vector128 AllBitsSet { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector64 vector = Vector64.AllBitsSet; @@ -52,11 +51,7 @@ public static Vector128 AllBitsSet public static int Count { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return Vector64.Count * 2; - } + get => Vector64.Count * 2; } /// Gets a new with the elements set to their index. @@ -107,7 +102,6 @@ public static bool IsSupported public static Vector128 One { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector64 vector = Vector64.One; @@ -120,7 +114,6 @@ public static Vector128 One public static Vector128 Zero { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -128,27 +121,14 @@ public static Vector128 Zero } } - internal string DisplayString - { - get - { - return IsSupported ? ToString() : SR.NotSupported_Type; - } - } + internal string DisplayString => IsSupported ? ToString() : SR.NotSupported_Type; /// Gets the element at the specified index. /// The index of the element to get. /// The value of the element at . /// was less than zero or greater than the number of elements. /// The type of the vector () is not supported. - public T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.GetElement(index); - } - } + public T this[int index] => this.GetElement(index); /// Adds two vectors to compute their sum. /// The vector to add with . @@ -258,12 +238,7 @@ public T this[int index] /// true if any elements in was not equal to the corresponding element in . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector128 left, Vector128 right) - { - return (left._lower != right._lower) - || (left._upper != right._upper); - } + public static bool operator !=(Vector128 left, Vector128 right) => !(left == right); /// Shifts each element of a vector left by the specified amount. /// The vector whose elements are to be shifted. @@ -380,7 +355,6 @@ public static Vector128 operator >>(Vector128 value, int shiftCount) /// /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 operator +(Vector128 value) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType(); @@ -405,14 +379,13 @@ public static Vector128 operator >>>(Vector128 value, int shiftCount) /// The object to compare with the current instance. /// true if is a and is equal to the current instance; otherwise, false. /// The type of the vector () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector128 other) && Equals(other); // Account for floating-point equality around NaN // This is in a separate method so it can be optimized by the mono interpreter/jiterpreter [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool EqualsFloatingPoint (Vector128 lhs, Vector128 rhs) + internal static bool EqualsFloatingPoint(Vector128 lhs, Vector128 rhs) { Vector128 result = Vector128.Equals(lhs, rhs) | ~(Vector128.Equals(lhs, lhs) | Vector128.Equals(rhs, rhs)); return result.AsInt32() == Vector128.AllBitsSet; @@ -464,7 +437,6 @@ public override int GetHashCode() /// Converts the current instance to an equivalent string representation. /// An equivalent string representation of the current instance. /// The type of the vector () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => ToString("G", CultureInfo.InvariantCulture); private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) @@ -692,15 +664,9 @@ private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] stri // New Surface Area // - static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector128 vector) - { - return Vector128.EqualsAny(vector, Vector128.AllBitsSet); - } + static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector128 vector) => Vector128.EqualsAny(vector, AllBitsSet); - static bool ISimdVector, T>.Any(Vector128 vector, T value) - { - return Vector128.EqualsAny(vector, Vector128.Create((T)value)); - } + static bool ISimdVector, T>.Any(Vector128 vector, T value) => Vector128.EqualsAny(vector, Vector128.Create(value)); static int ISimdVector, T>.IndexOfLastMatch(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 01591e0aa20cb..e8f16472b3695 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 @@ -75,7 +75,6 @@ public static Vector256 Abs(Vector256 vector) /// The sum of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Add(Vector256 left, Vector256 right) => left + right; /// Computes the bitwise-and of a given vector and the ones complement of another vector. @@ -107,7 +106,11 @@ public static Vector256 As(this Vector256 vector) ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); +#if MONO return Unsafe.As, Vector256>(ref vector); +#else + return Unsafe.BitCast, Vector256>(vector); +#endif } /// Reinterprets a as a new . @@ -116,7 +119,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsByte(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -125,7 +127,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsDouble(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -134,7 +135,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsInt16(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -143,7 +143,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsInt32(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -152,7 +151,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsInt64(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -161,7 +159,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsNInt(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -171,7 +168,6 @@ public static Vector256 As(this Vector256 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsNUInt(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -181,7 +177,6 @@ public static Vector256 As(this Vector256 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsSByte(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -190,7 +185,6 @@ public static Vector256 As(this Vector256 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsSingle(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -200,7 +194,6 @@ public static Vector256 As(this Vector256 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsUInt16(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -210,7 +203,6 @@ public static Vector256 As(this Vector256 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsUInt32(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -220,7 +212,6 @@ public static Vector256 As(this Vector256 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AsUInt64(this Vector256 vector) => vector.As(); /// Reinterprets a as a new . @@ -263,7 +254,6 @@ public static Vector AsVector(this Vector256 value) /// The bitwise-and of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 BitwiseAnd(Vector256 left, Vector256 right) => left & right; /// Computes the bitwise-or of two vectors. @@ -273,7 +263,6 @@ public static Vector AsVector(this Vector256 value) /// The bitwise-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 BitwiseOr(Vector256 left, Vector256 right) => left | right; /// Computes the ceiling of each element in a vector. @@ -294,7 +283,6 @@ internal static Vector256 Ceiling(Vector256 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Ceiling(Vector256 vector) => Ceiling(vector); /// Computes the ceiling of each element in a vector. @@ -302,7 +290,6 @@ internal static Vector256 Ceiling(Vector256 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Ceiling(Vector256 vector) => Ceiling(vector); /// Conditionally selects a value from two vectors on a bitwise basis. @@ -625,7 +612,6 @@ public static void CopyTo(this Vector256 vector, Span destination) /// A new with all elements initialized to . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(T value) { Vector128 vector = Vector128.Create(value); @@ -637,7 +623,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256i _mm256_set1_epi8 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -645,7 +630,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256d _mm256_set1_pd [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -653,7 +637,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256i _mm256_set1_epi16 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -661,7 +644,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256i _mm256_set1_epi32 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -669,14 +651,12 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256i _mm256_set1_epi64x [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -684,7 +664,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -693,7 +672,6 @@ public static Vector256 Create(T value) /// On x86, this method corresponds to __m256i _mm256_set1_epi8 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -701,7 +679,6 @@ public static Vector256 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m256 _mm256_set1_ps [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -710,7 +687,6 @@ public static Vector256 Create(T value) /// On x86, this method corresponds to __m256i _mm256_set1_epi16 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -719,7 +695,6 @@ public static Vector256 Create(T value) /// On x86, this method corresponds to __m256i _mm256_set1_epi32 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -728,7 +703,6 @@ public static Vector256 Create(T value) /// On x86, this method corresponds to __m256i _mm256_set1_epi64x [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(ulong value) => Create(value); /// Creates a new from a given array. @@ -1087,7 +1061,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the lower 128-bits will be initialized to. /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1095,14 +1068,12 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m256d _mm256_setr_m128d (__m128d lo, __m128d hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 128-bits will be initialized to. /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1110,21 +1081,18 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m256i _mm256_setr_m128i (__m128i lo, __m128i hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 128-bits will be initialized to. /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 128-bits will be initialized to. /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1132,7 +1100,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1140,7 +1107,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1148,7 +1114,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m256 _mm256_setr_m128 (__m128 lo, __m128 hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1156,7 +1121,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1165,7 +1129,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new initialized from and . /// On x86, this method corresponds to __m256i _mm256_setr_m128i (__m128i lo, __m128i hi) [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1173,7 +1136,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// The value that the upper 128-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Create(Vector128 lower, Vector128 upper) => Create(lower, upper); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1182,49 +1144,42 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(T value) => Vector128.CreateScalar(value).ToVector256(); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1232,7 +1187,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1240,14 +1194,12 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1255,7 +1207,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1263,7 +1214,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1271,7 +1221,6 @@ public static Vector256 Create(Vector128 lower, Vector128 upper) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1297,42 +1246,36 @@ public static Vector256 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1340,7 +1283,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1348,14 +1290,12 @@ public static Vector256 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1363,7 +1303,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1371,7 +1310,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1379,7 +1317,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. @@ -1398,7 +1335,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// The quotient of divided by . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Divide(Vector256 left, Vector256 right) => left / right; /// Divides a vector by a scalar to compute the per-element quotient. @@ -1407,7 +1343,6 @@ public static Vector256 CreateScalarUnsafe(T value) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Divide(Vector256 left, T right) => left / right; /// Computes the dot product of two vectors. @@ -1452,7 +1387,6 @@ public static Vector256 Equals(Vector256 left, Vector256 right) /// true if all elements in were equal to the corresponding element in . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsAll(Vector256 left, Vector256 right) => left == right; /// Compares two vectors to determine if any elements are equal. @@ -1536,7 +1470,6 @@ internal static Vector256 Floor(Vector256 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Floor(Vector256 vector) => Floor(vector); /// Computes the floor of each element in a vector. @@ -1544,7 +1477,6 @@ internal static Vector256 Floor(Vector256 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Floor(Vector256 vector) => Floor(vector); /// @@ -1594,7 +1526,6 @@ public static T GetElement(this Vector256 vector, int index) /// The value of the lower 128-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 GetLower(this Vector256 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -1607,7 +1538,6 @@ public static Vector128 GetLower(this Vector256 vector) /// The value of the upper 128-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 GetUpper(this Vector256 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -1798,7 +1728,6 @@ public static bool LessThanOrEqualAny(Vector256 left, Vector256 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. @@ -1829,7 +1758,6 @@ public static Vector256 LoadAligned(T* source) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LoadAlignedNonTemporal(T* source) => LoadAligned(source); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1866,17 +1794,13 @@ public static Vector256 LoadUnsafe(ref readonly T source, nuint elementOff /// Loads a vector from the given source and reinterprets it as . /// The source from which the vector will be loaded. /// The vector loaded from . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector256 LoadUnsafe(ref char source) => - LoadUnsafe(ref Unsafe.As(ref source)); + internal static Vector256 LoadUnsafe(ref char source) => LoadUnsafe(ref Unsafe.As(ref source)); /// Loads a vector from the given source and element offset and reinterprets it as . /// The source to which will be added before loading the vector. /// The element offset from from which the vector will be loaded. /// The vector loaded from plus . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector256 LoadUnsafe(ref char source, nuint elementOffset) => - LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + internal static Vector256 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1985,7 +1909,6 @@ public static Vector256 Min(Vector256 left, Vector256 right) /// The element-wise product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Multiply(Vector256 left, Vector256 right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -1995,7 +1918,6 @@ public static Vector256 Min(Vector256 left, Vector256 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Multiply(Vector256 left, T right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -2005,7 +1927,6 @@ public static Vector256 Min(Vector256 left, Vector256 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Multiply(T left, Vector256 right) => left * right; /// @@ -2138,7 +2059,6 @@ public static Vector256 Narrow(Vector256 lower, Vector256 up /// A vector whose elements are the negation of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Negate(Vector256 vector) => -vector; /// Computes the ones-complement of a vector. @@ -2147,7 +2067,6 @@ public static Vector256 Narrow(Vector256 lower, Vector256 up /// A vector whose elements are the ones-complement of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OnesComplement(Vector256 vector) { return Create( @@ -2161,7 +2080,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2169,7 +2087,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2177,7 +2094,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2185,7 +2101,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2193,7 +2108,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2201,7 +2115,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2210,7 +2123,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2219,7 +2131,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2228,7 +2139,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2237,7 +2147,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2246,7 +2155,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftLeft(Vector256 vector, int shiftCount) => vector << shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2254,7 +2162,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2262,7 +2169,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2270,7 +2176,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2278,7 +2183,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2286,7 +2190,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2295,7 +2198,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightArithmetic(Vector256 vector, int shiftCount) => vector >> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2303,7 +2205,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2311,7 +2212,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2319,7 +2219,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2327,7 +2226,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2335,7 +2233,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2343,7 +2240,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2352,7 +2248,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2361,7 +2256,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2370,7 +2264,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2379,7 +2272,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2388,7 +2280,6 @@ public static Vector256 OnesComplement(Vector256 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ShiftRightLogical(Vector256 vector, int shiftCount) => vector >>> shiftCount; /// Creates a new vector by selecting values from an input vector using a set of indices. @@ -2658,7 +2549,6 @@ public static Vector256 Sqrt(Vector256 vector) /// The type of and () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Store(this Vector256 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. @@ -2689,7 +2579,6 @@ public static void StoreAligned(this Vector256 source, T* destination) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StoreAlignedNonTemporal(this Vector256 source, T* destination) => source.StoreAligned(destination); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -2730,7 +2619,6 @@ public static void StoreUnsafe(this Vector256 source, ref T destination, n /// The difference of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Subtract(Vector256 left, Vector256 right) => left - right; /// Computes the sum of all elements in a vector. @@ -2757,7 +2645,6 @@ public static T Sum(Vector256 vector) /// A scalar containing the value of the first element. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ToScalar(this Vector256 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -3140,7 +3027,6 @@ public static Vector256 WithUpper(this Vector256 vector, Vector128 v /// The exclusive-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Xor(Vector256 left, Vector256 right) => left ^ right; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -3159,16 +3045,8 @@ internal static void SetElementUnsafe(in this Vector256 vector, int index, Unsafe.Add(ref address, index) = value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetLowerUnsafe(in this Vector256 vector, Vector128 value) - { - Unsafe.AsRef(in vector._lower) = value; - } + internal static void SetLowerUnsafe(in this Vector256 vector, Vector128 value) => Unsafe.AsRef(in vector._lower) = value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetUpperUnsafe(in this Vector256 vector, Vector128 value) - { - Unsafe.AsRef(in vector._upper) = value; - } + internal static void SetUpperUnsafe(in this Vector256 vector, Vector128 value) => Unsafe.AsRef(in vector._upper) = value; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs index d506b0da02776..36b704682d320 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs @@ -38,7 +38,6 @@ namespace System.Runtime.Intrinsics public static Vector256 AllBitsSet { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector128 vector = Vector128.AllBitsSet; @@ -51,11 +50,7 @@ public static Vector256 AllBitsSet public static int Count { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return Vector128.Count * 2; - } + get => Vector128.Count * 2; } /// Gets a new with the elements set to their index. @@ -106,7 +101,6 @@ public static bool IsSupported public static Vector256 One { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector128 vector = Vector128.One; @@ -119,7 +113,6 @@ public static Vector256 One public static Vector256 Zero { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -127,27 +120,14 @@ public static Vector256 Zero } } - internal string DisplayString - { - get - { - return IsSupported ? ToString() : SR.NotSupported_Type; - } - } + internal string DisplayString => IsSupported ? ToString() : SR.NotSupported_Type; /// Gets the element at the specified index. /// The index of the element to get. /// The value of the element at . /// was less than zero or greater than the number of elements. /// The type of the vector () is not supported. - public T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.GetElement(index); - } - } + public T this[int index] => this.GetElement(index); /// Adds two vectors to compute their sum. /// The vector to add with . @@ -257,12 +237,7 @@ public T this[int index] /// true if any elements in was not equal to the corresponding element in . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector256 left, Vector256 right) - { - return (left._lower != right._lower) - || (left._upper != right._upper); - } + public static bool operator !=(Vector256 left, Vector256 right) => !(left == right); /// Shifts each element of a vector left by the specified amount. /// The vector whose elements are to be shifted. @@ -314,7 +289,6 @@ public T this[int index] /// The product of and . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 operator *(T left, Vector256 right) => right * left; /// Computes the ones-complement of a vector. @@ -379,7 +353,6 @@ public static Vector256 operator >>(Vector256 value, int shiftCount) /// /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 operator +(Vector256 value) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector256BaseType(); @@ -403,7 +376,6 @@ public static Vector256 operator >>>(Vector256 value, int shiftCount) /// Determines whether the specified object is equal to the current instance. /// The object to compare with the current instance. /// true if is a and is equal to the current instance; otherwise, false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector256 other) && Equals(other); /// Determines whether the specified is equal to the current instance. @@ -454,7 +426,6 @@ public override int GetHashCode() /// Converts the current instance to an equivalent string representation. /// An equivalent string representation of the current instance. /// The type of the vector () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => ToString("G", CultureInfo.InvariantCulture); private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) @@ -682,15 +653,9 @@ private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] stri // New Surface Area // - static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector256 vector) - { - return Vector256.EqualsAny(vector, Vector256.AllBitsSet); - } + static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector256 vector) => Vector256.EqualsAny(vector, AllBitsSet); - static bool ISimdVector, T>.Any(Vector256 vector, T value) - { - return Vector256.EqualsAny(vector, Vector256.Create((T)value)); - } + static bool ISimdVector, T>.Any(Vector256 vector, T value) => Vector256.EqualsAny(vector, Vector256.Create(value)); static int ISimdVector, T>.IndexOfLastMatch(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 bf24a68ffc81e..8d44bf2899841 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 @@ -75,7 +75,6 @@ public static Vector512 Abs(Vector512 vector) /// The sum of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Add(Vector512 left, Vector512 right) => left + right; /// Computes the bitwise-and of a given vector and the ones complement of another vector. @@ -107,7 +106,11 @@ public static Vector512 As(this Vector512 vector) ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); +#if MONO return Unsafe.As, Vector512>(ref vector); +#else + return Unsafe.BitCast, Vector512>(vector); +#endif } /// Reinterprets a as a new . @@ -116,7 +119,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsByte(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -125,7 +127,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsDouble(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -134,7 +135,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsInt16(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -143,7 +143,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsInt32(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -152,7 +151,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsInt64(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -161,7 +159,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsNInt(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -171,7 +168,6 @@ public static Vector512 As(this Vector512 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsNUInt(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -181,7 +177,6 @@ public static Vector512 As(this Vector512 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsSByte(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -190,7 +185,6 @@ public static Vector512 As(this Vector512 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsSingle(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -200,7 +194,6 @@ public static Vector512 As(this Vector512 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsUInt16(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -210,7 +203,6 @@ public static Vector512 As(this Vector512 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsUInt32(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -220,7 +212,6 @@ public static Vector512 As(this Vector512 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 AsUInt64(this Vector512 vector) => vector.As(); /// Reinterprets a as a new . @@ -263,7 +254,6 @@ public static Vector AsVector(this Vector512 value) /// The bitwise-and of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 BitwiseAnd(Vector512 left, Vector512 right) => left & right; /// Computes the bitwise-or of two vectors. @@ -273,7 +263,6 @@ public static Vector AsVector(this Vector512 value) /// The bitwise-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 BitwiseOr(Vector512 left, Vector512 right) => left | right; /// Computes the ceiling of each element in a vector. @@ -294,7 +283,6 @@ internal static Vector512 Ceiling(Vector512 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Ceiling(Vector512 vector) => Ceiling(vector); /// Computes the ceiling of each element in a vector. @@ -302,7 +290,6 @@ internal static Vector512 Ceiling(Vector512 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Ceiling(Vector512 vector) => Ceiling(vector); /// Conditionally selects a value from two vectors on a bitwise basis. @@ -551,7 +538,6 @@ public static void CopyTo(this Vector512 vector, Span destination) /// A new with all elements initialized to . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(T value) { Vector256 vector = Vector256.Create(value); @@ -563,7 +549,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512i _mm512_set1_epi8 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -571,7 +556,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512d _mm512_set1_pd [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -579,7 +563,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512i _mm512_set1_epi16 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -587,7 +570,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512i _mm512_set1_epi32 [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -595,14 +577,12 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512i _mm512_set1_epi64x [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -610,7 +590,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -619,7 +598,6 @@ public static Vector512 Create(T value) /// On x86, this method corresponds to __m512i _mm512_set1_epi8 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -627,7 +605,6 @@ public static Vector512 Create(T value) /// A new with all elements initialized to . /// On x86, this method corresponds to __m512 _mm512_set1_ps [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -636,7 +613,6 @@ public static Vector512 Create(T value) /// On x86, this method corresponds to __m512i _mm512_set1_epi16 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -645,7 +621,6 @@ public static Vector512 Create(T value) /// On x86, this method corresponds to __m512i _mm512_set1_epi32 [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -654,7 +629,6 @@ public static Vector512 Create(T value) /// On x86, this method corresponds to __m512i _mm512_set1_epi64x [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(ulong value) => Create(value); /// Creates a new from a given array. @@ -1141,7 +1115,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the lower 256-bits will be initialized to. /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1149,14 +1122,12 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m512d _mm512_setr_m256d (__m256d lo, __m256d hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 256-bits will be initialized to. /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1164,21 +1135,18 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m512i _mm512_setr_m256i (__m256i lo, __m256i hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 256-bits will be initialized to. /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. /// The value that the lower 256-bits will be initialized to. /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1186,7 +1154,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1194,7 +1161,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1202,7 +1168,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . /// On x86, this method corresponds to __m512 _mm512_setr_m256 (__m256 lo, __m256 hi) - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1210,7 +1175,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1219,7 +1183,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// A new initialized from and . /// On x86, this method corresponds to __m512i _mm512_setr_m256i (__m256i lo, __m256i hi) [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance from two instances. @@ -1227,7 +1190,6 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that the upper 256-bits will be initialized to. /// A new initialized from and . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Create(Vector256 lower, Vector256 upper) => Create(lower, upper); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1235,84 +1197,84 @@ public static Vector512 Create(Vector256 lower, Vector256 upper) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. /// The type of () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(T value) => Vector256.CreateScalar(value).ToVector512(); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. + [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. + [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector512 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. + [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. + [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. + [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1338,42 +1300,36 @@ public static Vector512 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1381,7 +1337,6 @@ public static Vector512 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1389,14 +1344,12 @@ public static Vector512 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1404,7 +1357,6 @@ public static Vector512 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1412,7 +1364,6 @@ public static Vector512 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1420,7 +1371,6 @@ public static Vector512 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. @@ -1454,7 +1404,6 @@ public static Vector512 Divide(Vector512 left, Vector512 right) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Divide(Vector512 left, T right) => left / right; /// Computes the dot product of two vectors. @@ -1499,7 +1448,6 @@ public static Vector512 Equals(Vector512 left, Vector512 right) /// true if all elements in were equal to the corresponding element in . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsAll(Vector512 left, Vector512 right) => left == right; /// Compares two vectors to determine if any elements are equal. @@ -1583,7 +1531,6 @@ internal static Vector512 Floor(Vector512 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Floor(Vector512 vector) => Floor(vector); /// Computes the floor of each element in a vector. @@ -1591,7 +1538,6 @@ internal static Vector512 Floor(Vector512 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Floor(Vector512 vector) => Floor(vector); /// @@ -1641,7 +1587,6 @@ public static T GetElement(this Vector512 vector, int index) /// The value of the lower 256-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 GetLower(this Vector512 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -1654,7 +1599,6 @@ public static Vector256 GetLower(this Vector512 vector) /// The value of the upper 256-bits as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 GetUpper(this Vector512 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -1845,7 +1789,6 @@ public static bool LessThanOrEqualAny(Vector512 left, Vector512 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. @@ -1876,7 +1819,6 @@ public static Vector512 LoadAligned(T* source) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 LoadAlignedNonTemporal(T* source) => LoadAligned(source); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1913,17 +1855,13 @@ public static Vector512 LoadUnsafe(ref readonly T source, nuint elementOff /// Loads a vector from the given source and reinterprets it as . /// The source from which the vector will be loaded. /// The vector loaded from . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector512 LoadUnsafe(ref char source) => - LoadUnsafe(ref Unsafe.As(ref source)); + internal static Vector512 LoadUnsafe(ref char source) => LoadUnsafe(ref Unsafe.As(ref source)); /// Loads a vector from the given source and element offset and reinterprets it as . /// The source to which will be added before loading the vector. /// The element offset from from which the vector will be loaded. /// The vector loaded from plus . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector512 LoadUnsafe(ref char source, nuint elementOffset) => - LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + internal static Vector512 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2032,7 +1970,6 @@ public static Vector512 Min(Vector512 left, Vector512 right) /// The element-wise product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Multiply(Vector512 left, Vector512 right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -2042,7 +1979,6 @@ public static Vector512 Min(Vector512 left, Vector512 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Multiply(Vector512 left, T right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -2052,7 +1988,6 @@ public static Vector512 Min(Vector512 left, Vector512 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Multiply(T left, Vector512 right) => left * right; /// @@ -2184,7 +2119,6 @@ public static Vector512 Narrow(Vector512 lower, Vector512 up /// A vector whose elements are the negation of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Negate(Vector512 vector) => -vector; /// Computes the ones-complement of a vector. @@ -2193,7 +2127,6 @@ public static Vector512 Narrow(Vector512 lower, Vector512 up /// A vector whose elements are the ones-complement of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 OnesComplement(Vector512 vector) { return Create( @@ -2207,7 +2140,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2215,7 +2147,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2223,7 +2154,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2231,7 +2161,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2239,7 +2168,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2247,7 +2175,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2256,7 +2183,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2265,7 +2191,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2274,7 +2199,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2283,7 +2207,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2292,7 +2215,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftLeft(Vector512 vector, int shiftCount) => vector << shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2300,7 +2222,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2308,7 +2229,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2316,7 +2236,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2324,7 +2243,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2332,7 +2250,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2341,7 +2258,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightArithmetic(Vector512 vector, int shiftCount) => vector >> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2349,7 +2265,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2357,7 +2272,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2365,7 +2279,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2373,7 +2286,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2381,7 +2293,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2389,7 +2300,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2398,7 +2308,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2407,7 +2316,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2416,7 +2324,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2425,7 +2332,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2434,7 +2340,6 @@ public static Vector512 OnesComplement(Vector512 vector) /// A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ShiftRightLogical(Vector512 vector, int shiftCount) => vector >>> shiftCount; /// Creates a new vector by selecting values from an input vector using a set of indices. @@ -2704,7 +2609,6 @@ public static Vector512 Sqrt(Vector512 vector) /// The type of and () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Store(this Vector512 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. @@ -2735,7 +2639,6 @@ public static void StoreAligned(this Vector512 source, T* destination) /// This method may bypass the cache on certain platforms. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StoreAlignedNonTemporal(this Vector512 source, T* destination) => source.StoreAligned(destination); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -2776,7 +2679,6 @@ public static void StoreUnsafe(this Vector512 source, ref T destination, n /// The difference of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Subtract(Vector512 left, Vector512 right) => left - right; /// Computes the sum of all elements in a vector. @@ -2803,7 +2705,6 @@ public static T Sum(Vector512 vector) /// A scalar containing the value of the first element. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ToScalar(this Vector512 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -3152,7 +3053,6 @@ public static Vector512 WithUpper(this Vector512 vector, Vector256 v /// The exclusive-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Xor(Vector512 left, Vector512 right) => left ^ right; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -3171,16 +3071,8 @@ internal static void SetElementUnsafe(in this Vector512 vector, int index, Unsafe.Add(ref address, index) = value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetLowerUnsafe(in this Vector512 vector, Vector256 value) - { - Unsafe.AsRef(in vector._lower) = value; - } + internal static void SetLowerUnsafe(in this Vector512 vector, Vector256 value) => Unsafe.AsRef(in vector._lower) = value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetUpperUnsafe(in this Vector512 vector, Vector256 value) - { - Unsafe.AsRef(in vector._upper) = value; - } + internal static void SetUpperUnsafe(in this Vector512 vector, Vector256 value) => Unsafe.AsRef(in vector._upper) = value; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512_1.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512_1.cs index b070fc6e54dcb..235871dbe6069 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512_1.cs @@ -38,7 +38,6 @@ namespace System.Runtime.Intrinsics public static Vector512 AllBitsSet { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector256 vector = Vector256.AllBitsSet; @@ -51,11 +50,7 @@ public static Vector512 AllBitsSet public static int Count { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return Vector256.Count * 2; - } + get => Vector256.Count * 2; } /// Gets a new with the elements set to their index. @@ -106,7 +101,6 @@ public static bool IsSupported public static Vector512 One { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Vector256 vector = Vector256.One; @@ -119,7 +113,6 @@ public static Vector512 One public static Vector512 Zero { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -127,27 +120,14 @@ public static Vector512 Zero } } - internal string DisplayString - { - get - { - return IsSupported ? ToString() : SR.NotSupported_Type; - } - } + internal string DisplayString => IsSupported ? ToString() : SR.NotSupported_Type; /// Gets the element at the specified index. /// The index of the element to get. /// The value of the element at . /// was less than zero or greater than the number of elements. /// The type of the vector () is not supported. - public T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.GetElement(index); - } - } + public T this[int index] => this.GetElement(index); /// Adds two vectors to compute their sum. /// The vector to add with . @@ -257,12 +237,7 @@ public T this[int index] /// true if any elements in was not equal to the corresponding element in . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector512 left, Vector512 right) - { - return (left._lower != right._lower) - || (left._upper != right._upper); - } + public static bool operator !=(Vector512 left, Vector512 right) => !(left == right); /// Shifts each element of a vector left by the specified amount. /// The vector whose elements are to be shifted. @@ -314,7 +289,6 @@ public T this[int index] /// The product of and . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 operator *(T left, Vector512 right) => right * left; /// Computes the ones-complement of a vector. @@ -379,7 +353,6 @@ public static Vector512 operator >>(Vector512 value, int shiftCount) /// /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 operator +(Vector512 value) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector512BaseType(); @@ -403,7 +376,6 @@ public static Vector512 operator >>>(Vector512 value, int shiftCount) /// Determines whether the specified object is equal to the current instance. /// The object to compare with the current instance. /// true if is a and is equal to the current instance; otherwise, false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector512 other) && Equals(other); /// Determines whether the specified is equal to the current instance. @@ -454,7 +426,6 @@ public override int GetHashCode() /// Converts the current instance to an equivalent string representation. /// An equivalent string representation of the current instance. /// The type of the vector () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => ToString("G", CultureInfo.InvariantCulture); private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) @@ -682,15 +653,9 @@ private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] stri // New Surface Area // - static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector512 vector) - { - return Vector512.EqualsAny(vector, Vector512.AllBitsSet); - } + static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector512 vector) => Vector512.EqualsAny(vector, AllBitsSet); - static bool ISimdVector, T>.Any(Vector512 vector, T value) - { - return Vector512.EqualsAny(vector, Vector512.Create((T)value)); - } + static bool ISimdVector, T>.Any(Vector512 vector, T value) => Vector512.EqualsAny(vector, Vector512.Create(value)); static int ISimdVector, T>.IndexOfLastMatch(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 78b15f643ac28..743aad0a6503f 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 @@ -65,7 +65,6 @@ public static Vector64 Abs(Vector64 vector) /// The sum of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Add(Vector64 left, Vector64 right) => left + right; /// Computes the bitwise-and of a given vector and the ones complement of another vector. @@ -75,16 +74,7 @@ public static Vector64 Abs(Vector64 vector) /// The bitwise-and of and the ones-complement of . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector64 AndNot(Vector64 left, Vector64 right) - { - ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); - - Unsafe.SkipInit(out Vector64 result); - Unsafe.AsRef(in result._00) = left._00 & ~right._00; - - return result; - } + public static Vector64 AndNot(Vector64 left, Vector64 right) => left & ~right; /// Reinterprets a as a new . /// The type of the elements in the input vector. @@ -99,7 +89,11 @@ public static Vector64 As(this Vector64 vector) ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); +#if MONO return Unsafe.As, Vector64>(ref vector); +#else + return Unsafe.BitCast, Vector64>(vector); +#endif } /// Reinterprets a as a new . @@ -108,7 +102,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsByte(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -117,7 +110,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsDouble(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -126,7 +118,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsInt16(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -135,7 +126,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsInt32(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -144,7 +134,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsInt64(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -153,7 +142,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsNInt(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -163,7 +151,6 @@ public static Vector64 As(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsNUInt(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -173,7 +160,6 @@ public static Vector64 As(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsSByte(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -182,7 +168,6 @@ public static Vector64 As(this Vector64 vector) /// reinterpreted as a new . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsSingle(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -192,7 +177,6 @@ public static Vector64 As(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsUInt16(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -202,7 +186,6 @@ public static Vector64 As(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsUInt32(this Vector64 vector) => vector.As(); /// Reinterprets a as a new . @@ -212,7 +195,6 @@ public static Vector64 As(this Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 AsUInt64(this Vector64 vector) => vector.As(); /// Computes the bitwise-and of two vectors. @@ -222,7 +204,6 @@ public static Vector64 As(this Vector64 vector) /// The bitwise-and of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 BitwiseAnd(Vector64 left, Vector64 right) => left & right; /// Computes the bitwise-or of two vectors. @@ -232,7 +213,6 @@ public static Vector64 As(this Vector64 vector) /// The bitwise-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 BitwiseOr(Vector64 left, Vector64 right) => left | right; /// Computes the ceiling of each element in a vector. @@ -274,7 +254,6 @@ internal static Vector64 Ceiling(Vector64 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Ceiling(Vector64 vector) => Ceiling(vector); /// Computes the ceiling of each element in a vector. @@ -282,7 +261,6 @@ internal static Vector64 Ceiling(Vector64 vector) /// A vector whose elements are the ceiling of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Ceiling(Vector64 vector) => Ceiling(vector); /// Conditionally selects a value from two vectors on a bitwise basis. @@ -294,15 +272,7 @@ internal static Vector64 Ceiling(Vector64 vector) /// The type of , , and () is not supported. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector64 ConditionalSelect(Vector64 condition, Vector64 left, Vector64 right) - { - ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); - - Unsafe.SkipInit(out Vector64 result); - Unsafe.AsRef(in result._00) = (left._00 & condition._00) | (right._00 & ~condition._00); - - return result; - } + public static Vector64 ConditionalSelect(Vector64 condition, Vector64 left, Vector64 right) => (left & condition) | (right & ~condition); /// Converts a to a . /// The vector to convert. @@ -613,14 +583,12 @@ public static unsafe Vector64 Create(T value) /// On x86, this method corresponds to __m64 _mm_set1_pi8 /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(byte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(double value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -628,7 +596,6 @@ public static unsafe Vector64 Create(T value) /// On x86, this method corresponds to __m64 _mm_set1_pi16 /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(short value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -636,21 +603,18 @@ public static unsafe Vector64 Create(T value) /// On x86, this method corresponds to __m64 _mm_set1_pi32 /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(int value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(long value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(nint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -658,7 +622,6 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(nuint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -667,14 +630,12 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(sbyte value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. /// The value that all elements will be initialized to. /// A new with all elements initialized to . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(float value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -683,7 +644,6 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(ushort value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -692,7 +652,6 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(uint value) => Create(value); /// Creates a new instance with all elements initialized to the specified value. @@ -700,7 +659,6 @@ public static unsafe Vector64 Create(T value) /// A new with all elements initialized to . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Create(ulong value) => Create(value); /// Creates a new from a given array. @@ -918,42 +876,36 @@ public static unsafe Vector64 CreateScalar(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(byte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(double value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(short value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(int value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(long value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(nint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -961,7 +913,6 @@ public static unsafe Vector64 CreateScalar(T value) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(nuint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -969,14 +920,12 @@ public static unsafe Vector64 CreateScalar(T value) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(sbyte value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(float value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -984,7 +933,6 @@ public static unsafe Vector64 CreateScalar(T value) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(ushort value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -992,7 +940,6 @@ public static unsafe Vector64 CreateScalar(T value) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(uint value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements initialized to zero. @@ -1000,7 +947,6 @@ public static unsafe Vector64 CreateScalar(T value) /// A new instance with the first element initialized to and the remaining elements initialized to zero. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalar(ulong value) => CreateScalar(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1026,42 +972,36 @@ public static Vector64 CreateScalarUnsafe(T value) /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(byte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(double value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(short value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(int value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(long value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(nint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1069,7 +1009,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(nuint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1077,14 +1016,12 @@ public static Vector64 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(sbyte value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. /// The value that element 0 will be initialized to. /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(float value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1092,7 +1029,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(ushort value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1100,7 +1036,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(uint value) => CreateScalarUnsafe(value); /// Creates a new instance with the first element initialized to the specified value and the remaining elements left uninitialized. @@ -1108,7 +1043,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// A new instance with the first element initialized to and the remaining elements left uninitialized. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 CreateScalarUnsafe(ulong value) => CreateScalarUnsafe(value); /// Creates a new instance where the elements begin at a specified value and which are spaced apart according to another specified value. @@ -1127,7 +1061,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// The quotient of divided by . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Divide(Vector64 left, Vector64 right) => left / right; /// Divides a vector by a scalar to compute the per-element quotient. @@ -1136,7 +1069,6 @@ public static Vector64 CreateScalarUnsafe(T value) /// The type of the elements in the vector. /// The quotient of divided by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Divide(Vector64 left, T right) => left / right; /// Computes the dot product of two vectors. @@ -1146,34 +1078,7 @@ public static Vector64 CreateScalarUnsafe(T value) /// The dot product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Dot(Vector64 left, Vector64 right) - { - T result = default!; - - // Doing this as pairs is important for floating-point determinism - // This is because the underlying dpps instruction on x86/x64 will do this equivalently - // and otherwise the software vs accelerated implementations may differ in returned result. - - if (Vector64.Count != 1) - { - for (int index = 0; index < Vector64.Count; index += 2) - { - T value = Scalar.Add( - Scalar.Multiply(left.GetElementUnsafe(index + 0), right.GetElementUnsafe(index + 0)), - Scalar.Multiply(left.GetElementUnsafe(index + 1), right.GetElementUnsafe(index + 1)) - ); - - result = Scalar.Add(result, value); - } - } - else - { - result = Scalar.Multiply(left.GetElementUnsafe(0), right.GetElementUnsafe(0)); - } - - return result; - } + public static T Dot(Vector64 left, Vector64 right) => Sum(left * right); /// Compares two vectors to determine if they are equal on a per-element basis. /// The type of the elements in the vector. @@ -1203,7 +1108,6 @@ public static Vector64 Equals(Vector64 left, Vector64 right) /// true if all elements in were equal to the corresponding element in . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsAll(Vector64 left, Vector64 right) => left == right; /// Compares two vectors to determine if any elements are equal. @@ -1333,7 +1237,6 @@ internal static Vector64 Floor(Vector64 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Floor(Vector64 vector) => Floor(vector); /// Computes the floor of each element in a vector. @@ -1341,7 +1244,6 @@ internal static Vector64 Floor(Vector64 vector) /// A vector whose elements are the floor of the elements in . /// [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Floor(Vector64 vector) => Floor(vector); /// Computes ( * ) + , rounded as one ternary operation. @@ -1671,7 +1573,6 @@ public static bool LessThanOrEqualAny(Vector64 left, Vector64 right) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 Load(T* source) => LoadUnsafe(ref *source); /// Loads a vector from the given aligned source. @@ -1702,7 +1603,6 @@ public static unsafe Vector64 LoadAligned(T* source) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Vector64 LoadAlignedNonTemporal(T* source) => LoadAligned(source); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -1877,7 +1777,6 @@ public static Vector64 Min(Vector64 left, Vector64 right) /// The element-wise product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Multiply(Vector64 left, Vector64 right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -1887,7 +1786,6 @@ public static Vector64 Min(Vector64 left, Vector64 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Multiply(Vector64 left, T right) => left * right; /// Multiplies a vector by a scalar to compute their product. @@ -1897,7 +1795,6 @@ public static Vector64 Min(Vector64 left, Vector64 right) /// The product of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Multiply(T left, Vector64 right) => left * right; /// Computes an estimate of ( * ) + . @@ -2133,7 +2030,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements are the negation of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Negate(Vector64 vector) => -vector; /// Computes the ones-complement of a vector. @@ -2142,7 +2038,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements are the ones-complement of the corresponding elements in . /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 OnesComplement(Vector64 vector) => ~vector; /// Shifts each element of a vector left by the specified amount. @@ -2150,7 +2045,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2158,7 +2052,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2166,7 +2059,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2174,7 +2066,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2182,7 +2073,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2190,7 +2080,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted left by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2199,7 +2088,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2208,7 +2096,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2217,7 +2104,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2226,7 +2112,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts each element of a vector left by the specified amount. @@ -2235,7 +2120,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted left by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftLeft(Vector64 vector, int shiftCount) => vector << shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2243,7 +2127,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2251,7 +2134,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2259,7 +2141,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2267,7 +2148,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2275,7 +2155,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (signed) each element of a vector right by the specified amount. @@ -2284,7 +2163,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightArithmetic(Vector64 vector, int shiftCount) => vector >> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2292,7 +2170,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2300,7 +2177,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2308,7 +2184,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2316,7 +2191,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2324,7 +2198,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2332,7 +2205,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64The number of bits by which to shift each element. /// A vector whose elements where shifted right by . [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2341,7 +2213,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2350,7 +2221,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2359,7 +2229,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2368,7 +2237,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Shifts (unsigned) each element of a vector right by the specified amount. @@ -2377,7 +2245,6 @@ public static unsafe Vector64 Narrow(Vector64 lower, Vector64A vector whose elements where shifted right by . [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ShiftRightLogical(Vector64 vector, int shiftCount) => vector >>> shiftCount; /// Creates a new vector by selecting values from an input vector using a set of indices. @@ -2579,7 +2446,6 @@ public static Vector64 Sqrt(Vector64 vector) /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Store(this Vector64 source, T* destination) => source.StoreUnsafe(ref *destination); /// Stores a vector at the given aligned destination. @@ -2610,7 +2476,6 @@ public static unsafe void StoreAligned(this Vector64 source, T* destinatio /// The type of () is not supported. [Intrinsic] [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void StoreAlignedNonTemporal(this Vector64 source, T* destination) => source.StoreAligned(destination); #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ('T') @@ -2651,7 +2516,6 @@ public static void StoreUnsafe(this Vector64 source, ref T destination, nu /// The difference of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Subtract(Vector64 left, Vector64 right) => left - right; /// Computes the sum of all elements in a vector. @@ -2679,7 +2543,6 @@ public static T Sum(Vector64 vector) /// A scalar containing the value of the first element. /// The type of () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ToScalar(this Vector64 vector) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); @@ -3074,7 +2937,6 @@ public static Vector64 WithElement(this Vector64 vector, int index, T v /// The exclusive-or of and . /// The type of and () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Xor(Vector64 left, Vector64 right) => left ^ right; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs index 362977a3b2275..23730bb5b0090 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs @@ -56,7 +56,6 @@ public static Vector64 AllBitsSet public static unsafe int Count { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); @@ -113,12 +112,7 @@ public static bool IsSupported public static Vector64 One { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T scalar = Scalar.One; - return Vector64.Create(scalar); - } + get => Vector64.Create(Scalar.One); } /// Gets a new with all elements initialized to zero. @@ -126,7 +120,6 @@ public static Vector64 One public static Vector64 Zero { [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); @@ -134,27 +127,14 @@ public static Vector64 Zero } } - internal string DisplayString - { - get - { - return IsSupported ? ToString() : SR.NotSupported_Type; - } - } + internal string DisplayString => IsSupported ? ToString() : SR.NotSupported_Type; /// Gets the element at the specified index. /// The index of the element to get. /// The value of the element at . /// was less than zero or greater than the number of elements. /// The type of the vector () is not supported. - public T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.GetElement(index); - } - } + public T this[int index] => this.GetElement(index); /// Adds two vectors to compute their sum. /// The vector to add with . @@ -291,18 +271,7 @@ public T this[int index] /// true if any elements in was not equal to the corresponding element in . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Vector64 left, Vector64 right) - { - for (int index = 0; index < Count; index++) - { - if (!Scalar.Equals(left.GetElementUnsafe(index), right.GetElementUnsafe(index))) - { - return true; - } - } - return false; - } + public static bool operator !=(Vector64 left, Vector64 right) => !(left == right); /// Shifts each element of a vector left by the specified amount. /// The vector whose elements are to be shifted. @@ -369,7 +338,6 @@ public T this[int index] /// The product of and . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 operator *(T left, Vector64 right) => right * left; /// Computes the ones-complement of a vector. @@ -432,7 +400,6 @@ public static Vector64 operator >>(Vector64 value, int shiftCount) /// A vector whose elements are the unary negation of the corresponding elements in . /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 operator -(Vector64 vector) => Zero - vector; /// Returns a given vector unchanged. @@ -440,7 +407,6 @@ public static Vector64 operator >>(Vector64 value, int shiftCount) /// /// The type of the vector () is not supported. [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 operator +(Vector64 value) { ThrowHelper.ThrowForUnsupportedIntrinsicsVector64BaseType(); @@ -469,7 +435,6 @@ public static Vector64 operator >>>(Vector64 value, int shiftCount) /// Determines whether the specified object is equal to the current instance. /// The object to compare with the current instance. /// true if is a and is equal to the current instance; otherwise, false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals([NotNullWhen(true)] object? obj) => (obj is Vector64 other) && Equals(other); /// Determines whether the specified is equal to the current instance. @@ -529,7 +494,6 @@ public override int GetHashCode() /// Converts the current instance to an equivalent string representation. /// An equivalent string representation of the current instance. /// The type of the vector () is not supported. - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => ToString("G", CultureInfo.InvariantCulture); private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? formatProvider) @@ -757,15 +721,9 @@ private string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] stri // New Surface Area // - static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector64 vector) - { - return Vector64.EqualsAny(vector, Vector64.AllBitsSet); - } + static bool ISimdVector, T>.AnyWhereAllBitsSet(Vector64 vector) => Vector64.EqualsAny(vector, AllBitsSet); - static bool ISimdVector, T>.Any(Vector64 vector, T value) - { - return Vector64.EqualsAny(vector, Vector64.Create((T)value)); - } + static bool ISimdVector, T>.Any(Vector64 vector, T value) => Vector64.EqualsAny(vector, Vector64.Create(value)); static int ISimdVector, T>.IndexOfLastMatch(Vector64 vector) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs index 6e08d95d2e400..e02192a70ebbe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs @@ -23,7 +23,7 @@ public static class SearchValues ///
/// The set of values. /// The optimized representation of used for efficient searching. - public static SearchValues Create(ReadOnlySpan values) + public static SearchValues Create(params ReadOnlySpan values) { if (values.IsEmpty) { @@ -66,7 +66,7 @@ public static SearchValues Create(ReadOnlySpan values) ///
/// The set of values. /// /// The optimized representation of used for efficient searching. - public static SearchValues Create(ReadOnlySpan values) + public static SearchValues Create(params ReadOnlySpan values) { if (values.IsEmpty) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 88c923892309c..7ee394a0d8f4c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -349,33 +349,33 @@ public override int GetHashCode() public override string ToString() { - return Number.FormatSingle(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(m_value, null, NumberFormatInfo.CurrentInfo); } public string ToString(IFormatProvider? provider) { - return Number.FormatSingle(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(m_value, null, NumberFormatInfo.GetInstance(provider)); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { - return Number.FormatSingle(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatFloat(m_value, format, NumberFormatInfo.CurrentInfo); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { - return Number.FormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider)); } public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } /// public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); } // Parses a float from a String in the given style. If @@ -2217,6 +2217,10 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static ulong IBinaryFloatParseAndFormatInfo.FloatToBits(float value) => BitConverter.SingleToUInt32Bits(value); + static int IBinaryFloatParseAndFormatInfo.MaxRoundTripDigits => 9; + + static int IBinaryFloatParseAndFormatInfo.MaxPrecisionCustomFormat => 7; + // // Helpers // diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index 691b2eb558f35..62fcdc2cd2712 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -58,7 +58,7 @@ public Span(T[]? array) /// at 'start' index and ending at 'end' index (exclusive). ///
/// The target array. - /// The index at which to begin the span. + /// The zero-based index at which to begin the span. /// The number of items in the span. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. @@ -139,7 +139,7 @@ internal Span(ref T reference, int length) /// /// Returns a reference to specified element of the Span. /// - /// + /// The zero-based index. /// /// /// Thrown when index less than 0 or index greater than or equal to Length @@ -376,7 +376,7 @@ public override string ToString() /// /// Forms a slice out of the given span, beginning at 'start'. /// - /// The index at which to begin this slice. + /// The zero-based index at which to begin this slice. /// /// Thrown when the specified index is not in range (<0 or >Length). /// @@ -392,7 +392,7 @@ public Span Slice(int start) /// /// Forms a slice out of the given span, beginning at 'start', of given length /// - /// The index at which to begin this slice. + /// The zero-based index at which to begin this slice. /// The desired length for the slice (exclusive). /// /// Thrown when the specified or end index is not in range (<0 or >Length). diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs index 356903063d156..40b9b131ea86f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -811,25 +812,37 @@ internal static int GetHashCodeOrdinalIgnoreCase(ReadOnlySpan value) return Marvin.ComputeHash32OrdinalIgnoreCase(ref MemoryMarshal.GetReference(value), value.Length /* in chars, not bytes */, (uint)seed, (uint)(seed >> 32)); } - // Use this if and only if 'Denial of Service' attacks are not a concern (i.e. never used for free-form user input), - // or are otherwise mitigated + // Important GetNonRandomizedHashCode{OrdinalIgnoreCase} notes: + // + // Use if and only if 'Denial of Service' attacks are not a concern (i.e. never used for free-form user input), + // or are otherwise mitigated. + // + // The string-based implementation relies on System.String being null terminated. All reads are performed + // two characters at a time, so for odd-length strings, the final read will include the null terminator. + // This implementation must not be used as-is with spans, or otherwise arbitrary char refs/pointers, as + // they're not guaranteed to be null-terminated. + // + // For spans, we must produce the exact same value as is used for strings: consumers like Dictionary<> + // rely on str.GetNonRandomizedHashCode() == GetNonRandomizedHashCode(str.AsSpan()). As such, we must + // restructure the comparison so that for odd-length spans, we simulate the null terminator and include + // it in the hash computation exactly as does str.GetNonRandomizedHashCode(). + internal unsafe int GetNonRandomizedHashCode() { fixed (char* src = &_firstChar) { - Debug.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'"); + Debug.Assert(src[Length] == '\0', "src[Length] == '\\0'"); Debug.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary"); uint hash1 = (5381 << 16) + 5381; uint hash2 = hash1; uint* ptr = (uint*)src; - int length = this.Length; + int length = Length; while (length > 2) { length -= 4; - // Where length is 4n-1 (e.g. 3,7,11,15,19) this additionally consumes the null terminator hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ ptr[0]; hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ ptr[1]; ptr += 2; @@ -837,7 +850,6 @@ internal unsafe int GetNonRandomizedHashCode() if (length > 0) { - // Where length is 4n-3 (e.g. 1,5,9,13,17) this additionally consumes the null terminator hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ ptr[0]; } @@ -845,23 +857,77 @@ internal unsafe int GetNonRandomizedHashCode() } } + internal static unsafe int GetNonRandomizedHashCode(ReadOnlySpan span) + { + uint hash1 = (5381 << 16) + 5381; + uint hash2 = hash1; + + int length = span.Length; + fixed (char* src = &MemoryMarshal.GetReference(span)) + { + uint* ptr = (uint*)src; + + LengthSwitch: + switch (length) + { + default: + do + { + length -= 4; + hash1 = BitOperations.RotateLeft(hash1, 5) + hash1 ^ Unsafe.ReadUnaligned(ptr); + hash2 = BitOperations.RotateLeft(hash2, 5) + hash2 ^ Unsafe.ReadUnaligned(ptr + 1); + ptr += 2; + } + while (length >= 4); + goto LengthSwitch; + + case 3: + hash1 = BitOperations.RotateLeft(hash1, 5) + hash1 ^ Unsafe.ReadUnaligned(ptr); + uint p1 = *(char*)(ptr + 1); +#if BIGENDIAN + p1 <<= 16; +#endif + hash2 = BitOperations.RotateLeft(hash2, 5) + hash2 ^ p1; + break; + + case 2: + hash2 = BitOperations.RotateLeft(hash2, 5) + hash2 ^ Unsafe.ReadUnaligned(ptr); + break; + + case 1: + uint p0 = *(char*)ptr; +#if BIGENDIAN + p0 <<= 16; +#endif + hash2 = BitOperations.RotateLeft(hash2, 5) + hash2 ^ p0; + break; + + case 0: + break; + } + } + + return (int)(hash1 + (hash2 * 1_566_083_941)); + } + + // We "normalize to lowercase" every char by ORing with 0x0020. This casts + // a very wide net because it will change, e.g., '^' to '~'. But that should + // be ok because we expect this to be very rare in practice. These are valid + // for both for big-endian and for little-endian. + private const uint NormalizeToLowercase = 0x0020_0020u; + internal unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCase() { uint hash1 = (5381 << 16) + 5381; uint hash2 = hash1; + int length = Length; fixed (char* src = &_firstChar) { - Debug.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'"); + Debug.Assert(src[Length] == '\0', "src[this.Length] == '\\0'"); Debug.Assert(((int) src) % 4 == 0, "Managed string should start at 4 bytes boundary"); uint* ptr = (uint*) src; - int length = this.Length; - - // We "normalize to lowercase" every char by ORing with 0x0020. This casts - // a very wide net because it will change, e.g., '^' to '~'. But that should - // be ok because we expect this to be very rare in practice. - const uint NormalizeToLowercase = 0x0020_0020u; // valid both for big-endian and for little-endian while (length > 2) { @@ -873,7 +939,6 @@ internal unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCase() } length -= 4; - // Where length is 4n-1 (e.g. 3,7,11,15,19) this additionally consumes the null terminator hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (p0 | NormalizeToLowercase); hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p1 | NormalizeToLowercase); ptr += 2; @@ -887,7 +952,6 @@ internal unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCase() goto NotAscii; } - // Where length is 4n-3 (e.g. 1,5,9,13,17) this additionally consumes the null terminator hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p0 | NormalizeToLowercase); } } @@ -895,48 +959,131 @@ internal unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCase() return (int)(hash1 + (hash2 * 1566083941)); NotAscii: - return GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow(this); + return GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow(hash1, hash2, this.AsSpan(Length - length)); + } - static int GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow(string str) - { - int length = str.Length; - char[]? borrowedArr = null; - // Important: leave an additional space for '\0' - Span scratch = (uint)length < 64 ? - stackalloc char[64] : (borrowedArr = ArrayPool.Shared.Rent(length + 1)); + internal static unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCase(ReadOnlySpan span) + { + uint hash1 = (5381 << 16) + 5381; + uint hash2 = hash1; - int charsWritten = Ordinal.ToUpperOrdinal(str, scratch); - Debug.Assert(charsWritten == length); - scratch[length] = '\0'; + uint p0, p1; + int length = span.Length; - const uint NormalizeToLowercase = 0x0020_0020u; - uint hash1 = (5381 << 16) + 5381; - uint hash2 = hash1; + fixed (char* src = &MemoryMarshal.GetReference(span)) + { + uint* ptr = (uint*)src; - // Duplicate the main loop, can be removed once JIT gets "Loop Unswitching" optimization - fixed (char* src = scratch) + LengthSwitch: + switch (length) { - uint* ptr = (uint*)src; - while (length > 2) - { - length -= 4; - hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (ptr[0] | NormalizeToLowercase); - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (ptr[1] | NormalizeToLowercase); - ptr += 2; - } + default: + do + { + p0 = Unsafe.ReadUnaligned(ptr); + p1 = Unsafe.ReadUnaligned(ptr + 1); + if (!Utf16Utility.AllCharsInUInt32AreAscii(p0 | p1)) + { + goto NotAscii; + } + + length -= 4; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (p0 | NormalizeToLowercase); + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p1 | NormalizeToLowercase); + ptr += 2; + } + while (length >= 4); + goto LengthSwitch; + + case 3: + p0 = Unsafe.ReadUnaligned(ptr); + p1 = *(char*)(ptr + 1); +#if BIGENDIAN + p1 <<= 16; +#endif + if (!Utf16Utility.AllCharsInUInt32AreAscii(p0 | p1)) + { + goto NotAscii; + } + + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (p0 | NormalizeToLowercase); + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p1 | NormalizeToLowercase); + break; + + case 2: + p0 = Unsafe.ReadUnaligned(ptr); + if (!Utf16Utility.AllCharsInUInt32AreAscii(p0)) + { + goto NotAscii; + } + + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p0 | NormalizeToLowercase); + break; + + case 1: + p0 = *(char*)ptr; +#if BIGENDIAN + p0 <<= 16; +#endif + if (p0 > 0x7f) + { + goto NotAscii; + } + + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (p0 | NormalizeToLowercase); + break; + + case 0: + break; + } + } - if (length > 0) - { - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (ptr[0] | NormalizeToLowercase); - } + return (int)(hash1 + (hash2 * 1566083941)); + + NotAscii: + return GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow(hash1, hash2, span.Slice(span.Length - length)); + } + + private static unsafe int GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow(uint hash1, uint hash2, ReadOnlySpan str) + { + int length = str.Length; + + // We allocate one char more than the length to accomodate a null terminator. + // That lets the reading always be performed two characters at a time, as odd-length + // inputs will have a final terminator to backstop the last read. + char[]? borrowedArr = null; + Span scratch = (uint)length < 256 ? + stackalloc char[256] : + (borrowedArr = ArrayPool.Shared.Rent(length + 1)); + + int charsWritten = Ordinal.ToUpperOrdinal(str, scratch); + Debug.Assert(charsWritten == length); + scratch[length] = '\0'; + + // Duplicate the main loop, can be removed once JIT gets "Loop Unswitching" optimization + fixed (char* src = scratch) + { + uint* ptr = (uint*)src; + while (length > 2) + { + length -= 4; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (ptr[0] | NormalizeToLowercase); + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (ptr[1] | NormalizeToLowercase); + ptr += 2; } - if (borrowedArr != null) + if (length > 0) { - ArrayPool.Shared.Return(borrowedArr); + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (ptr[0] | NormalizeToLowercase); } - return (int)(hash1 + (hash2 * 1566083941)); } + + if (borrowedArr != null) + { + ArrayPool.Shared.Return(borrowedArr); + } + + return (int)(hash1 + (hash2 * 1566083941)); } // Determines whether a specified string is a prefix of the current instance diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs index d20d5d2364901..4ea9b5b37b67f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.cs @@ -320,6 +320,7 @@ private static unsafe string Ctor(ReadOnlySpan value) } public static string Create(int length, TState state, SpanAction action) + where TState : allows ref struct { if (action is null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs index 78ebf6c9c3c5d..27f67b1a8e88e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs @@ -210,7 +210,7 @@ public int GetHashCode(object obj) [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public sealed class CultureAwareComparer : StringComparer, ISerializable + public sealed class CultureAwareComparer : StringComparer, IAlternateEqualityComparer, string?>, ISerializable { internal static readonly CultureAwareComparer InvariantCaseSensitiveInstance = new CultureAwareComparer(CompareInfo.Invariant, CompareOptions.None); internal static readonly CultureAwareComparer InvariantIgnoreCaseInstance = new CultureAwareComparer(CompareInfo.Invariant, CompareOptions.IgnoreCase); @@ -268,6 +268,23 @@ public override int GetHashCode(string obj) return _compareInfo.GetHashCode(obj, _options); } + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + _compareInfo.GetHashCode(span, _options); + + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return _compareInfo.Compare(span, target, _options) == 0; + } + + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); + // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) { @@ -299,7 +316,7 @@ private protected override bool IsWellKnownCultureAwareComparerCore([NotNullWhen [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public class OrdinalComparer : StringComparer + public class OrdinalComparer : StringComparer, IAlternateEqualityComparer, string?> { private readonly bool _ignoreCase; // Do not rename (binary serialization) @@ -358,6 +375,27 @@ public override int GetHashCode(string obj) return obj.GetHashCode(); } + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return _ignoreCase? + span.EqualsOrdinalIgnoreCase(target) : + span.SequenceEqual(target); + } + + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + _ignoreCase ? + string.GetHashCodeOrdinalIgnoreCase(span) : + string.GetHashCode(span); + + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); + // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) { @@ -382,7 +420,7 @@ private protected override bool IsWellKnownOrdinalComparerCore(out bool ignoreCa } [Serializable] - internal sealed class OrdinalCaseSensitiveComparer : OrdinalComparer, ISerializable + internal sealed class OrdinalCaseSensitiveComparer : OrdinalComparer, ISerializable, IAlternateEqualityComparer, string?> { internal static readonly OrdinalCaseSensitiveComparer Instance = new OrdinalCaseSensitiveComparer(); @@ -403,6 +441,23 @@ public override int GetHashCode(string obj) return obj.GetHashCode(); } + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return span.SequenceEqual(target); + } + + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + string.GetHashCode(span); + + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); + public void GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(OrdinalComparer)); @@ -411,7 +466,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) } [Serializable] - internal sealed class OrdinalIgnoreCaseComparer : OrdinalComparer, ISerializable + internal sealed class OrdinalIgnoreCaseComparer : OrdinalComparer, IAlternateEqualityComparer, string?>, ISerializable { internal static readonly OrdinalIgnoreCaseComparer Instance = new OrdinalIgnoreCaseComparer(); @@ -468,6 +523,23 @@ public override int GetHashCode(string obj) return obj.GetHashCodeOrdinalIgnoreCase(); } + bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan span, string? target) + { + // See explanation in StringEqualityComparer.Equals. + if (span.IsEmpty && target is null) + { + return false; + } + + return span.EqualsOrdinalIgnoreCase(target); + } + + int IAlternateEqualityComparer, string?>.GetHashCode(ReadOnlySpan span) => + string.GetHashCodeOrdinalIgnoreCase(span); + + string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => + span.ToString(); + public void GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(OrdinalComparer)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs index 76d2701b0eaab..b879feb073784 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.Wasm; using System.Runtime.Intrinsics.X86; namespace System.Text @@ -1651,7 +1652,7 @@ private static bool AllCharsInVectorAreAscii(Vector512 vector) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 ExtractAsciiVector(Vector128 vectorFirst, Vector128 vectorSecond) + internal static Vector128 ExtractAsciiVector(Vector128 vectorFirst, Vector128 vectorSecond) { // Narrows two vectors of words [ w7 w6 w5 w4 w3 w2 w1 w0 ] and [ w7' w6' w5' w4' w3' w2' w1' w0' ] // to a vector of bytes [ b7 ... b0 b7' ... b0']. @@ -1665,12 +1666,32 @@ private static Vector128 ExtractAsciiVector(Vector128 vectorFirst, { return AdvSimd.Arm64.UnzipEven(vectorFirst.AsByte(), vectorSecond.AsByte()); } + else if (PackedSimd.IsSupported) + { + return PackedSimd.ConvertNarrowingSaturateUnsigned(vectorFirst.AsInt16(), vectorSecond.AsInt16()); + } else { return Vector128.Narrow(vectorFirst, vectorSecond); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ExtractAsciiVector(Vector256 vectorFirst, Vector256 vectorSecond) + { + return Avx2.IsSupported + ? PackedSpanHelpers.FixUpPackedVector256Result(Avx2.PackUnsignedSaturate(vectorFirst.AsInt16(), vectorSecond.AsInt16())) + : Vector256.Narrow(vectorFirst, vectorSecond); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ExtractAsciiVector(Vector512 vectorFirst, Vector512 vectorSecond) + { + return Avx512BW.IsSupported + ? PackedSpanHelpers.FixUpPackedVector512Result(Avx512BW.PackUnsignedSaturate(vectorFirst.AsInt16(), vectorSecond.AsInt16())) + : Vector512.Narrow(vectorFirst, vectorSecond); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe nuint NarrowUtf16ToAscii_Intrinsified(char* pUtf16Buffer, byte* pAsciiBuffer, nuint elementCount) { @@ -1821,7 +1842,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_256(char* pUtf16Buff // Turn the 16 ASCII chars we just read into 16 ASCII bytes, then copy it to the destination. ref byte asciiBuffer = ref *pAsciiBuffer; - Vector256 asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst); + Vector256 asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, 0); nuint currentOffsetInElements = Vector256.Size / 2; // we processed 16 elements so far @@ -1847,7 +1868,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_256(char* pUtf16Buff } // Turn the 16 ASCII chars we just read into 16 ASCII bytes, then copy it to the destination. - asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements); } @@ -1877,7 +1898,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_256(char* pUtf16Buff // Build up the ASCII vector and perform the store. Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector256.Size == 0, "Write should be aligned."); - asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorSecond); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorSecond); asciiVector.StoreUnsafe(ref asciiBuffer, currentOffsetInElements); currentOffsetInElements += Vector256.Size; @@ -1900,7 +1921,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_256(char* pUtf16Buff // First part was all ASCII, narrow and aligned write. Note we're only filling in the low half of the vector. Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector128.Size == 0, "Destination should be 128-bit-aligned."); - asciiVector = Vector256.Narrow(utf16VectorFirst, utf16VectorFirst); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements); currentOffsetInElements += Vector256.Size / 2; @@ -1938,7 +1959,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_512(char* pUtf16Buff // Turn the 32 ASCII chars we just read into 32 ASCII bytes, then copy it to the destination. ref byte asciiBuffer = ref *pAsciiBuffer; - Vector512 asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst); + Vector512 asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, 0); // how to store the lower part of a avx512 nuint currentOffsetInElements = Vector512.Size / 2; // we processed 32 elements so far @@ -1965,7 +1986,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_512(char* pUtf16Buff } // Turn the 32 ASCII chars we just read into 32 ASCII bytes, then copy it to the destination. - asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements); } @@ -1995,7 +2016,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_512(char* pUtf16Buff // Build up the ASCII vector and perform the store. Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector512.Size == 0, "Write should be aligned."); - asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorSecond); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorSecond); asciiVector.StoreUnsafe(ref asciiBuffer, currentOffsetInElements); currentOffsetInElements += Vector512.Size; @@ -2018,7 +2039,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Intrinsified_512(char* pUtf16Buff // First part was all ASCII, narrow and aligned write. Note we're only filling in the low half of the vector. Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % Vector256.Size == 0, "Destination should be 256-bit-aligned."); - asciiVector = Vector512.Narrow(utf16VectorFirst, utf16VectorFirst); + asciiVector = ExtractAsciiVector(utf16VectorFirst, utf16VectorFirst); asciiVector.GetLower().StoreUnsafe(ref asciiBuffer, currentOffsetInElements); currentOffsetInElements += Vector512.Size / 2; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 7a33d7d0ed3a7..11b76d7b19568 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -846,7 +846,8 @@ private StringBuilder AppendCore(StringBuilder value, int startIndex, int count) if (length == 0) { ExpandByABlock(count); - length = Math.Min(m_ChunkChars.Length - m_ChunkLength, count); + Debug.Assert(m_ChunkLength == 0 && m_ChunkChars.Length >= count); + length = count; } value.CopyTo(startIndex, new Span(m_ChunkChars, m_ChunkLength, length), length); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 6447466e33593..f993a8a9b62c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -16,7 +16,7 @@ namespace System.Threading // public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private readonly object? _lock; + private readonly Lock? _lock; private bool _unregistering; // Handle to this object to keep it alive @@ -30,7 +30,7 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO { Debug.Assert(ThreadPool.UseWindowsThreadPool); - _lock = new object(); + _lock = new Lock(); waitHandle.DangerousAddRef(); _waitHandle = waitHandle; @@ -194,7 +194,7 @@ private void FinishUnregisteringAsync(object? waitObject) // If this object gets resurrected and another thread calls Unregister, that creates a race condition. // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && Monitor.TryEnter(_lock)) + if ((_lock != null) && _lock.TryEnter()) { try { @@ -218,7 +218,7 @@ private void FinishUnregisteringAsync(object? waitObject) } finally { - Monitor.Exit(_lock); + _lock.Exit(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs index aa0c2e4478ca6..0f0eafe3fe068 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs @@ -51,9 +51,12 @@ public class ThreadLocal : IDisposable // when the instance is disposed. private volatile bool _initialized; - // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock. + // IdManager assigns and reuses slot IDs. private static readonly IdManager s_idManager = new IdManager(); + // Global Lock for the IdManager. + private static readonly Lock s_idManagerLock = new Lock(); + // A linked list of all values associated with this ThreadLocal instance. // We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field. private LinkedSlot? _linkedSlot = new LinkedSlot(null); @@ -165,7 +168,7 @@ protected virtual void Dispose(bool disposing) { int id; - lock (s_idManager) + lock (s_idManagerLock) { id = ~_idComplement; _idComplement = 0; @@ -384,7 +387,7 @@ private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value) var linkedSlot = new LinkedSlot(slotArray); // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array - lock (s_idManager) + lock (s_idManagerLock) { // Check that the instance has not been disposed. It is important to check this under a lock, since // Dispose also executes under a lock. @@ -525,7 +528,7 @@ private static void GrowTable(ref LinkedSlotVolatile[] table, int minLength) // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while // the value continues to be referenced from the new (larger) array. // - lock (s_idManager) + lock (s_idManagerLock) { for (int i = 0; i < table.Length; i++) { @@ -614,13 +617,15 @@ private sealed class IdManager // Stores IDs that are used, and if each ID tracksAllValues or not. private readonly Dictionary _usedIdToTracksAllValuesMap = new Dictionary(); - // Stores IDs that were previously used and are now free to reuse. Additionally, the object is also used as a lock - // for the IdManager. + // Stores IDs that were previously used and are now free to reuse. private readonly List _freeIds = new List(); + // Lock for the IdManager. + private readonly Lock _freeIdsLock = new Lock(); + internal int GetId(bool trackAllValues) { - lock (_freeIds) + lock (_freeIdsLock) { int availableId; int freeIdCount = _freeIds.Count; @@ -658,7 +663,7 @@ internal int GetId(bool trackAllValues) // Identify if an allocated id tracks all values or not internal bool IdTracksAllValues(int id) { - lock (_freeIds) + lock (_freeIdsLock) { return _usedIdToTracksAllValuesMap.TryGetValue(id, out bool tracksAllValues) && tracksAllValues; } @@ -669,7 +674,7 @@ internal bool IdTracksAllValues(int id) // Return an ID to the pool internal void ReturnId(int id, bool idTracksAllValues) { - lock (_freeIds) + lock (_freeIdsLock) { if (!idTracksAllValues) _idsThatDoNotTrackAllValues--; @@ -728,7 +733,7 @@ internal FinalizationHelper(LinkedSlotVolatile[] slotArray) { // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to // the table will be have been removed, and so the table can get GC'd. - lock (s_idManager) + lock (s_idManagerLock) { // If the slot wasn't disposed between reading it above and entering the lock // decrement idsThatDoNotTrackAllValuesCountRemaining diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 7eef607ef1069..365a68c4489ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -404,12 +404,30 @@ public int Count private readonly int[] _assignedWorkItemQueueThreadCounts = s_assignableWorkItemQueueCount > 0 ? new int[s_assignableWorkItemQueueCount] : Array.Empty(); + private object? _nextWorkItemToProcess; + + // The scheme works as follows: + // - From NotScheduled, the only transition is to Scheduled when new items are enqueued and a thread is requested to process them. + // - From Scheduled, the only transition is to Determining right before trying to dequeue an item. + // - From Determining, it can go to either NotScheduled when no items are present in the queue (the previous thread processed all of them) + // or Scheduled if the queue is still not empty (let the current thread handle parallelization as convinient). + // + // The goal is to avoid requesting more threads than necessary, while still ensuring that all items are processed. + // Another thread isn't requested hastily while the state is Determining, + // instead the parallelizer takes care of that. We also ensure that only one thread can be parallelizing at any time. + private enum QueueProcessingStage + { + NotScheduled, + Determining, + Scheduled + } + [StructLayout(LayoutKind.Sequential)] private struct CacheLineSeparated { private readonly Internal.PaddingFor32 pad1; - public int hasOutstandingThreadRequest; + public int queueProcessingStage; private readonly Internal.PaddingFor32 pad2; } @@ -573,24 +591,16 @@ public void RefreshLoggingEnabledFull() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void EnsureThreadRequested() { - // Only one thread is requested at a time to avoid over-parallelization - if (Interlocked.CompareExchange(ref _separated.hasOutstandingThreadRequest, 1, 0) == 0) + // Only request a thread if the stage is NotScheduled. + // Otherwise let the current requested thread handle parallelization. + if (Interlocked.Exchange( + ref _separated.queueProcessingStage, + (int)QueueProcessingStage.Scheduled) == (int)QueueProcessingStage.NotScheduled) { ThreadPool.RequestWorkerThread(); } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void MarkThreadRequestSatisfied() - { - // The change needs to be visible to other threads that may request a worker thread before a work item is attempted - // to be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not request a - // thread because it sees that a thread request is already outstanding, and the current thread is the last thread - // processing work items, the current thread must see the work item queued by the enqueuer. - _separated.hasOutstandingThreadRequest = 0; - Interlocked.MemoryBarrier(); - } - public void Enqueue(object callback, bool forceGlobal) { Debug.Assert((callback is IThreadPoolWorkItem) ^ (callback is Task)); @@ -645,6 +655,15 @@ internal static bool LocalFindAndPop(object callback) return workItem; } + if (_nextWorkItemToProcess != null) + { + workItem = Interlocked.Exchange(ref _nextWorkItemToProcess, null); + if (workItem != null) + { + return workItem; + } + } + // Check for high-priority work items if (tl.isProcessingHighPriorityWorkItems) { @@ -763,6 +782,32 @@ public long GlobalCount // Dispatch (if YieldFromDispatchLoop is true), or performing periodic activities public const uint DispatchQuantumMs = 30; + private static object? DequeueWithPriorityAlternation(ThreadPoolWorkQueue workQueue, ThreadPoolWorkQueueThreadLocals tl, out bool missedSteal) + { + object? workItem = null; + + // Alternate between checking for high-prioriy and normal-priority work first, that way both sets of work + // items get a chance to run in situations where worker threads are starved and work items that run also + // take over the thread, sustaining starvation. For example, when worker threads are continually starved, + // high-priority work items may always be queued and normal-priority work items may not get a chance to run. + bool dispatchNormalPriorityWorkFirst = workQueue._dispatchNormalPriorityWorkFirst; + if (dispatchNormalPriorityWorkFirst && !tl.workStealingQueue.CanSteal) + { + workQueue._dispatchNormalPriorityWorkFirst = !dispatchNormalPriorityWorkFirst; + ConcurrentQueue queue = + s_assignableWorkItemQueueCount > 0 ? tl.assignedGlobalWorkItemQueue : workQueue.workItems; + if (!queue.TryDequeue(out workItem) && s_assignableWorkItemQueueCount > 0) + { + workQueue.workItems.TryDequeue(out workItem); + } + } + + missedSteal = false; + workItem ??= workQueue.Dequeue(tl, ref missedSteal); + + return workItem; + } + /// /// Dispatches work items to this thread. /// @@ -780,66 +825,128 @@ internal static bool Dispatch() workQueue.AssignWorkItemQueue(tl); } - // Before dequeuing the first work item, acknowledge that the thread request has been satisfied - workQueue.MarkThreadRequestSatisfied(); + // The change needs to be visible to other threads that may request a worker thread before a work item is attempted + // to be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not request a + // thread because it sees a Determining or Scheduled stage, and the current thread is the last thread processing + // work items, the current thread must either see the work item queued by the enqueuer, or it must see a stage of + // Scheduled, and try to dequeue again or request another thread. + Debug.Assert(workQueue._separated.queueProcessingStage == (int)QueueProcessingStage.Scheduled); + workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining; + Interlocked.MemoryBarrier(); object? workItem = null; + if (workQueue._nextWorkItemToProcess != null) { - // Alternate between checking for high-prioriy and normal-priority work first, that way both sets of work - // items get a chance to run in situations where worker threads are starved and work items that run also - // take over the thread, sustaining starvation. For example, when worker threads are continually starved, - // high-priority work items may always be queued and normal-priority work items may not get a chance to run. - bool dispatchNormalPriorityWorkFirst = workQueue._dispatchNormalPriorityWorkFirst; - if (dispatchNormalPriorityWorkFirst && !tl.workStealingQueue.CanSteal) - { - workQueue._dispatchNormalPriorityWorkFirst = !dispatchNormalPriorityWorkFirst; - ConcurrentQueue queue = - s_assignableWorkItemQueueCount > 0 ? tl.assignedGlobalWorkItemQueue : workQueue.workItems; - if (!queue.TryDequeue(out workItem) && s_assignableWorkItemQueueCount > 0) - { - workQueue.workItems.TryDequeue(out workItem); - } - } + workItem = Interlocked.Exchange(ref workQueue._nextWorkItemToProcess, null); + } - if (workItem == null) + if (workItem == null) + { + // Try to dequeue a work item, clean up and return if no item was found + while ((workItem = DequeueWithPriorityAlternation(workQueue, tl, out bool missedSteal)) == null) { - bool missedSteal = false; - workItem = workQueue.Dequeue(tl, ref missedSteal); - - if (workItem == null) + // + // No work. + // If we missed a steal, though, there may be more work in the queue. + // Instead of looping around and trying again, we'll just request another thread. Hopefully the thread + // that owns the contended work-stealing queue will pick up its own workitems in the meantime, + // which will be more efficient than this thread doing it anyway. + // + if (missedSteal) { if (s_assignableWorkItemQueueCount > 0) { workQueue.UnassignWorkItemQueue(tl); } - // - // No work. - // If we missed a steal, though, there may be more work in the queue. - // Instead of looping around and trying again, we'll just request another thread. Hopefully the thread - // that owns the contended work-stealing queue will pick up its own workitems in the meantime, - // which will be more efficient than this thread doing it anyway. - // - if (missedSteal) + Debug.Assert(workQueue._separated.queueProcessingStage != (int)QueueProcessingStage.NotScheduled); + workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Scheduled; + ThreadPool.RequestWorkerThread(); + return true; + } + + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have scheduled a work item to process the work, so try to dequeue a work item again. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref workQueue._separated.queueProcessingStage, + (int)QueueProcessingStage.NotScheduled, + (int)QueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)QueueProcessingStage.Determining) + { + if (s_assignableWorkItemQueueCount > 0) { - workQueue.EnsureThreadRequested(); + workQueue.UnassignWorkItemQueue(tl); } - // Tell the VM we're returning normally, not because Hill Climbing asked us to return. return true; } + + // A work item was enqueued after the stage was set to Determining earlier, and a thread was not requested + // by the enqueuer. Set the stage back to Determining and try to dequeue a work item again. + // + // See the first similarly used memory barrier in the method for why it's necessary. + workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining; + Interlocked.MemoryBarrier(); } + } + + { + // A work item may have been enqueued after the stage was set to Determining earlier, so the stage may be + // Scheduled here, and the enqueued work item may have already been dequeued above or by a different thread. Now + // that we're about to try dequeuing a second work item, set the stage back to Determining first so that we'll + // be able to detect if an enqueue races with the dequeue below. + // + // See the first similarly used memory barrier in the method for why it's necessary. + workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Determining; + Interlocked.MemoryBarrier(); - // A work item was successfully dequeued, and there may be more work items to process. Request a thread to - // parallelize processing of work items, before processing more work items. Following this, it is the - // responsibility of the new thread and other enqueuers to request more threads as necessary. The - // parallelization may be necessary here for correctness (aside from perf) if the work item blocks for some - // reason that may have a dependency on other queued work items. - workQueue.EnsureThreadRequested(); + object? secondWorkItem = DequeueWithPriorityAlternation(workQueue, tl, out bool missedSteal); + if (secondWorkItem != null) + { + Debug.Assert(workQueue._nextWorkItemToProcess == null); + workQueue._nextWorkItemToProcess = secondWorkItem; + } - // After this point, this method is no longer responsible for ensuring thread requests except for missed steals + if (secondWorkItem != null || missedSteal) + { + // A work item was successfully dequeued, and there may be more work items to process. Request a thread to + // parallelize processing of work items, before processing more work items. Following this, it is the + // responsibility of the new thread and other enqueuers to request more threads as necessary. The + // parallelization may be necessary here for correctness (aside from perf) if the work item blocks for some + // reason that may have a dependency on other queued work items. + Debug.Assert(workQueue._separated.queueProcessingStage != (int)QueueProcessingStage.NotScheduled); + workQueue._separated.queueProcessingStage = (int)QueueProcessingStage.Scheduled; + ThreadPool.RequestWorkerThread(); + } + else + { + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have requested a thread, so request one now. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref workQueue._separated.queueProcessingStage, + (int)QueueProcessingStage.NotScheduled, + (int)QueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)QueueProcessingStage.Scheduled) + { + // A work item was enqueued after the stage was set to Determining earlier, and a thread was not + // requested by the enqueuer, so request a thread now. An alternate is to retry dequeuing, as requesting + // a thread can be more expensive, but retrying multiple times (though unlikely) can delay the + // processing of the first work item that was already dequeued. + ThreadPool.RequestWorkerThread(); + } + } } + // + // After this point, this method is no longer responsible for ensuring thread requests except for missed steals + // + // Has the desire for logging changed since the last time we entered? workQueue.RefreshLoggingEnabled(); @@ -1070,7 +1177,23 @@ internal sealed class ThreadPoolTypedWorkItemQueue : IThreadPoolWo where T : struct where TCallback : struct, IThreadPoolTypedWorkItemQueueCallback { - private int _isScheduledForProcessing; + // The scheme works as follows: + // - From NotScheduled, the only transition is to Scheduled when new items are enqueued and a TP work item is enqueued to process them. + // - From Scheduled, the only transition is to Determining right before trying to dequeue an item. + // - From Determining, it can go to either NotScheduled when no items are present in the queue (the previous TP work item processed all of them) + // or Scheduled if the queue is still not empty (let the current TP work item handle parallelization as convinient). + // + // The goal is to avoid enqueueing more TP work items than necessary, while still ensuring that all items are processed. + // Another TP work item isn't enqueued to the thread pool hastily while the state is Determining, + // instead the parallelizer takes care of that. We also ensure that only one thread can be parallelizing at any time. + private enum QueueProcessingStage + { + NotScheduled, + Determining, + Scheduled + } + + private int _queueProcessingStage; private readonly ConcurrentQueue _workItems = new ConcurrentQueue(); public int Count => _workItems.Count; @@ -1082,40 +1205,81 @@ public void Enqueue(T workItem) } public void BatchEnqueue(T workItem) => _workItems.Enqueue(workItem); - public void CompleteBatchEnqueue() => ScheduleForProcessing(); - - private void ScheduleForProcessing() + public void CompleteBatchEnqueue() { - // Only one thread is requested at a time to avoid over-parallelization. Currently where this type is used, queued - // work is expected to be processed at high priority. The implementation could be modified to support different - // priorities if necessary. - if (Interlocked.CompareExchange(ref _isScheduledForProcessing, 1, 0) == 0) + // Only enqueue a work item if the stage is NotScheduled. + // Otherwise there must be a work item already queued or another thread already handling parallelization. + if (Interlocked.Exchange( + ref _queueProcessingStage, + (int)QueueProcessingStage.Scheduled) == (int)QueueProcessingStage.NotScheduled) { ThreadPool.UnsafeQueueHighPriorityWorkItemInternal(this); } } - void IThreadPoolWorkItem.Execute() + private void UpdateQueueProcessingStage(bool isQueueEmpty) { - Debug.Assert(_isScheduledForProcessing != 0); + if (!isQueueEmpty) + { + // There are more items to process, set stage to Scheduled and enqueue a TP work item. + _queueProcessingStage = (int)QueueProcessingStage.Scheduled; + } + else + { + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have scheduled a work item to process the work, so schedule one one. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref _queueProcessingStage, + (int)QueueProcessingStage.NotScheduled, + (int)QueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)QueueProcessingStage.Determining) + { + return; + } + } - // This change needs to be visible to other threads that may enqueue work items before a work item is attempted to - // be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not schedule for - // processing, and the current thread is the last thread processing work items, the current thread must see the work - // item queued by the enqueuer. - _isScheduledForProcessing = 0; - Interlocked.MemoryBarrier(); - if (!_workItems.TryDequeue(out T workItem)) + ThreadPool.UnsafeQueueHighPriorityWorkItemInternal(this); + } + + void IThreadPoolWorkItem.Execute() + { + T workItem; + while (true) { - return; + Debug.Assert(_queueProcessingStage == (int)QueueProcessingStage.Scheduled); + + // The change needs to be visible to other threads that may request a worker thread before a work item is attempted + // to be dequeued by the current thread. In particular, if an enqueuer queues a work item and does not request a + // thread because it sees a Determining or Scheduled stage, and the current thread is the last thread processing + // work items, the current thread must either see the work item queued by the enqueuer, or it must see a stage of + // Scheduled, and try to dequeue again or request another thread. + _queueProcessingStage = (int)QueueProcessingStage.Determining; + Interlocked.MemoryBarrier(); + + if (_workItems.TryDequeue(out workItem)) + { + break; + } + + // The stage here would be Scheduled if an enqueuer has enqueued work and changed the stage, or Determining + // otherwise. If the stage is Determining, there's no more work to do. If the stage is Scheduled, the enqueuer + // would not have scheduled a work item to process the work, so try to dequeue a work item again. + int stageBeforeUpdate = + Interlocked.CompareExchange( + ref _queueProcessingStage, + (int)QueueProcessingStage.NotScheduled, + (int)QueueProcessingStage.Determining); + Debug.Assert(stageBeforeUpdate != (int)QueueProcessingStage.NotScheduled); + if (stageBeforeUpdate == (int)QueueProcessingStage.Determining) + { + return; + } } - // An work item was successfully dequeued, and there may be more work items to process. Schedule a work item to - // parallelize processing of work items, before processing more work items. Following this, it is the responsibility - // of the new work item and the poller thread to schedule more work items as necessary. The parallelization may be - // necessary here if the user callback as part of handling the work item blocks for some reason that may have a - // dependency on other queued work items. - ScheduleForProcessing(); + UpdateQueueProcessingStage(_workItems.IsEmpty); ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals!; Debug.Assert(tl != null); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 346259aa412c3..f125dcb4b4de3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -20,6 +20,8 @@ internal sealed partial class TimerQueue : IThreadPoolWorkItem /// private static readonly AutoResetEvent s_timerEvent = new AutoResetEvent(false); + private static readonly Lock s_timerEventLock = new Lock(); + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback private bool _isScheduled; private long _scheduledDueTimeMs; @@ -50,7 +52,9 @@ private bool SetTimerPortable(uint actualDuration) Debug.Assert((int)actualDuration >= 0); long dueTimeMs = TickCount64 + (int)actualDuration; AutoResetEvent timerEvent = s_timerEvent; - lock (timerEvent) + Lock timerEventLock = s_timerEventLock; + + lock (timerEventLock) { if (!_isScheduled) { @@ -74,9 +78,11 @@ private bool SetTimerPortable(uint actualDuration) private static void TimerThread() { AutoResetEvent timerEvent = s_timerEvent; + Lock timerEventLock = s_timerEventLock; List timersToFire = s_scheduledTimersToFire!; List timers; - lock (timerEvent) + + lock (timerEventLock) { timers = s_scheduledTimers!; } @@ -88,7 +94,7 @@ private static void TimerThread() long currentTimeMs = TickCount64; shortestWaitDurationMs = int.MaxValue; - lock (timerEvent) + lock (timerEventLock) { for (int i = timers.Count - 1; i >= 0; --i) { diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f742fe7a77e1c..ef7d3adbc0a1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1052,6 +1052,8 @@ private static string GetArgumentName(ExceptionArgument argument) return "divisor"; case ExceptionArgument.factor: return "factor"; + case ExceptionArgument.set: + return "set"; default: Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); return ""; @@ -1232,6 +1234,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_ExpectedAsciiDigit; case ExceptionResource.Argument_HasToBeArrayClass: return SR.Argument_HasToBeArrayClass; + case ExceptionResource.InvalidOperation_IncompatibleComparer: + return SR.InvalidOperation_IncompatibleComparer; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1342,6 +1346,7 @@ internal enum ExceptionArgument arrayType, divisor, factor, + set, } // @@ -1428,5 +1433,6 @@ internal enum ExceptionResource Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, Argument_HasToBeArrayClass, + InvalidOperation_IncompatibleComparer, } } diff --git a/src/libraries/System.Private.Uri/src/System/UriHelper.cs b/src/libraries/System.Private.Uri/src/System/UriHelper.cs index fbdfd0314a3ee..3b45b59c03deb 100644 --- a/src/libraries/System.Private.Uri/src/System/UriHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/UriHelper.cs @@ -12,13 +12,11 @@ internal static class UriHelper { public static unsafe string SpanToLowerInvariantString(ReadOnlySpan span) { -#pragma warning disable CS8500 // takes address of managed type - return string.Create(span.Length, (IntPtr)(&span), static (buffer, spanPtr) => + return string.Create(span.Length, span, static (buffer, span) => { - int charsWritten = (*(ReadOnlySpan*)spanPtr).ToLowerInvariant(buffer); + int charsWritten = span.ToLowerInvariant(buffer); Debug.Assert(charsWritten == buffer.Length); }); -#pragma warning restore CS8500 } // http://host/Path/Path/File?Query is the base of @@ -618,12 +616,10 @@ internal static unsafe string StripBidiControlCharacters(ReadOnlySpan strT return backingString ?? new string(strToClean); } -#pragma warning disable CS8500 // takes address of managed type - ReadOnlySpan tmpStrToClean = strToClean; // avoid address exposing the span and impacting the other code in the method that uses it - return string.Create(tmpStrToClean.Length - charsToRemove, (IntPtr)(&tmpStrToClean), static (buffer, strToCleanPtr) => + return string.Create(strToClean.Length - charsToRemove, strToClean, static (buffer, strToClean) => { int destIndex = 0; - foreach (char c in *(ReadOnlySpan*)strToCleanPtr) + foreach (char c in strToClean) { if (!IsBidiControlCharacter(c)) { @@ -632,7 +628,6 @@ internal static unsafe string StripBidiControlCharacters(ReadOnlySpan strT } Debug.Assert(buffer.Length == destIndex); }); -#pragma warning restore CS8500 } } } diff --git a/src/libraries/System.Private.Xml/src/Misc/HResults.cs b/src/libraries/System.Private.Xml/src/Misc/HResults.cs index dcd8e40424200..d926214738b48 100644 --- a/src/libraries/System.Private.Xml/src/Misc/HResults.cs +++ b/src/libraries/System.Private.Xml/src/Misc/HResults.cs @@ -4,7 +4,7 @@ /* These HRESULTs are used for mapping managed exceptions to COM error codes and vice versa through COM Interop. For background on COM error codes see -https://docs.microsoft.com/en-us/windows/desktop/com/com-error-codes. +https://learn.microsoft.com/windows/desktop/com/com-error-codes. FACILITY_URT is defined as 0x13 (0x8013xxxx). The facility range is reserved for the .NET Framework SDK teams. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs index 6510eb4defe9c..0c37d6c05f13a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs @@ -32,7 +32,7 @@ internal sealed class DecimalFormatter private readonly char _zeroDigit; // These characters have special meaning for CLR and must be escaped - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings + // https://learn.microsoft.com/dotnet/standard/base-types/custom-numeric-format-strings private const string ClrSpecialChars = "0#.,%\u2030Ee\\'\";"; // This character is used to escape literal (passive) digits '0'..'9' diff --git a/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml b/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml index f79155e70fc36..d353b22c21ec8 100644 --- a/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index c2588f7fde331..8aa6ea597b228 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -23,7 +23,7 @@ public DelegatingAssembly(Assembly assembly) } #pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly - [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'", Url = "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } @@ -111,19 +111,19 @@ public override Type[] GetExportedTypes() } #pragma warning disable IL3003 // netstandard2.1 didn't have RequiresAssemblyFiles attributes applied on Assembly - [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream? GetFile(string name) { return UnderlyingAssembly.GetFile(name); } - [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app", Url = "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles() { return UnderlyingAssembly.GetFiles(); } - [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles(bool)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] + [RequiresAssemblyFiles("Calling 'System.Reflection.Assembly.GetFiles(bool)' will throw for assemblies embedded in a single-file app", Url = "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles(bool getResourceModules) { return UnderlyingAssembly.GetFiles(getResourceModules); diff --git a/src/libraries/System.Reflection.DispatchProxy/src/Resources/Strings.resx b/src/libraries/System.Reflection.DispatchProxy/src/Resources/Strings.resx index 73f8a5ed0f400..f87d7b205aed6 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.DispatchProxy/src/Resources/Strings.resx @@ -1,4 +1,64 @@ + + @@ -60,6 +120,9 @@ The base type '{0}' cannot be sealed. + + DispatchProxy does not support invocation of static virtual members. + The base type '{0}' must have a public parameterless constructor. diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs index d6ba3df9a9f81..6153f7e0cfdbd 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs +++ b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs @@ -425,7 +425,11 @@ private MethodBuilder AddMethodImpl(MethodInfo mi, int methodInfoIndex) paramReqMods[i] = parameters[i].GetRequiredCustomModifiers(); } - MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, + MethodAttributes attributes = MethodAttributes.Public; + + attributes |= mi.IsStatic ? MethodAttributes.Static : MethodAttributes.Virtual; + + MethodBuilder mdb = _tb.DefineMethod(mi.Name, attributes, CallingConventions.Standard, mi.ReturnType, null, null, paramTypes, paramReqMods, null); @@ -445,6 +449,18 @@ private MethodBuilder AddMethodImpl(MethodInfo mi, int methodInfoIndex) } ILGenerator il = mdb.GetILGenerator(); + if (mi.IsStatic) + { + ConstructorInfo exCtor = typeof(NotSupportedException).GetConstructor([typeof(string)])!; + + il.Emit(OpCodes.Ldstr, SR.DispatchProxy_Method_Invocation_Cannot_Be_Static); + il.Emit(OpCodes.Newobj, exCtor); + il.Emit(OpCodes.Throw); + + _tb.DefineMethodOverride(mdb, mi); + return mdb; + } + ParametersArray args = new ParametersArray(il, paramTypes); // object[] args = new object[paramCount]; diff --git a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs index df16833b724e2..4cc5a96423510 100644 --- a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs +++ b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs @@ -557,6 +557,39 @@ public static void Proxy_Declares_Interface_Properties(bool useGenericCreate) Assert.NotNull(propertyInfo); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Proxy_Declares_Interface_Static_Virtual_Properties(bool useGenericCreate) + { + TestType_IStaticVirtualPropertyService proxy = CreateHelper(useGenericCreate); + PropertyInfo? propertyInfo = proxy.GetType().GetTypeInfo().GetDeclaredProperty(nameof(TestType_IStaticVirtualPropertyService.TestProperty)); + Assert.NotNull(propertyInfo); + Assert.True(propertyInfo.GetMethod!.IsStatic); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Proxy_Declares_Interface_Static_Virtual_Methods(bool useGenericCreate) + { + TestType_IStaticVirtualMethodService proxy = CreateHelper(useGenericCreate); + MethodInfo? methodInfo = proxy.GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestType_IStaticVirtualMethodService.TestMethod)); + Assert.NotNull(methodInfo); + Assert.True(methodInfo.IsStatic); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Invoke_Static_Virtual_Method_Throws_NotSupportedException(bool useGenericCreate) + { + TestType_IStaticVirtualMethodService proxy = CreateHelper(useGenericCreate); + MethodInfo? methodInfo = proxy.GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestType_IStaticVirtualMethodService.TestMethod)); + Assert.NotNull(methodInfo); + Assert.Throws(() => methodInfo.Invoke(proxy, BindingFlags.DoNotWrapExceptions, null, null, null)); + } + #if NET [Fact] public static void Invoke_Event_Add_And_Remove_And_Raise_Invokes_Correct_Methods_Generic_And_Non_Generic_Tests() diff --git a/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs b/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs index 16637c9a67d65..6f71ce492e7bf 100644 --- a/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs +++ b/src/libraries/System.Reflection.DispatchProxy/tests/TestTypes.cs @@ -63,12 +63,24 @@ public interface TestType_IOneWay void OneWay(); } +// Demonstrates proxies can be made for static virtual methods. +public interface TestType_IStaticVirtualMethodService +{ + static virtual void TestMethod() { } +} + // Demonstrates proxies can be made for properties. public interface TestType_IPropertyService { string ReadWrite { get; set; } } +// Demonstrates proxies can be made for static virtual properties. +public interface TestType_IStaticVirtualPropertyService +{ + static virtual string TestProperty { get; set; } +} + // Demonstrates proxies can be made for events. public interface TestType_IEventService { 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 995ab5679fc73..afd65c03d6bad 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -261,7 +261,7 @@ protected GenericTypeParameterBuilder() { } public override System.Reflection.ConstructorInfo[] GetConstructors(System.Reflection.BindingFlags bindingAttr) { throw null; } public override object[] GetCustomAttributes(bool inherit) { throw null; } public override object[] GetCustomAttributes(System.Type attributeType, bool inherit) { throw null; } - public override System.Type GetElementType() { throw null; } + public override System.Type? GetElementType() { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)] public override System.Reflection.EventInfo GetEvent(string name, System.Reflection.BindingFlags bindingAttr) { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)] 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 046043177986b..1686beb569b80 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 @@ -95,7 +95,7 @@ public override Type[] GetGenericParameterConstraints() => protected override bool IsCOMObjectImpl() => false; protected override bool HasElementTypeImpl() => false; protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.Public; - public override Type GetElementType() => throw new NotSupportedException(); + public override Type? GetElementType() => null; public override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(); public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(); public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(); 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 b78526adda13c..111c24eb6a48a 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 @@ -688,13 +688,21 @@ private EntityHandle GetTypeReferenceOrSpecificationHandle(Type type) { if (!_typeReferences.TryGetValue(type, out var typeHandle)) { - if (type.IsArray || type.IsGenericParameter || (type.IsGenericType && !type.IsGenericTypeDefinition)) + if (type.HasElementType || type.IsGenericParameter || + (type.IsGenericType && !type.IsGenericTypeDefinition)) { typeHandle = AddTypeSpecification(type); } else { - typeHandle = AddTypeReference(type, GetResolutionScopeHandle(type)); + if (type.IsNested) + { + typeHandle = AddTypeReference(GetTypeReferenceOrSpecificationHandle(type.DeclaringType!), null, type.Name); + } + else + { + typeHandle = AddTypeReference(GetAssemblyReference(type.Assembly), type.Namespace, type.Name); + } } _typeReferences.Add(type, typeHandle); @@ -703,16 +711,6 @@ private EntityHandle GetTypeReferenceOrSpecificationHandle(Type type) return typeHandle; } - private EntityHandle GetResolutionScopeHandle(Type type) - { - if (type.IsNested) - { - return GetTypeReferenceOrSpecificationHandle(type.DeclaringType!); - } - - return GetAssemblyReference(type.Assembly); - } - private TypeSpecificationHandle AddTypeSpecification(Type type) => _metadataBuilder.AddTypeSpecification( signature: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetTypeSpecificationSignature(type, this))); @@ -943,11 +941,11 @@ private MethodDefinitionHandle AddMethodDefinition(MethodBuilderImpl method, Blo bodyOffset: offset, parameterList: MetadataTokens.ParameterHandle(parameterToken)); - private TypeReferenceHandle AddTypeReference(Type type, EntityHandle resolutionScope) => + private TypeReferenceHandle AddTypeReference(EntityHandle resolutionScope, string? ns, string name) => _metadataBuilder.AddTypeReference( resolutionScope: resolutionScope, - @namespace: (type.Namespace == null) ? default : _metadataBuilder.GetOrAddString(type.Namespace), - name: _metadataBuilder.GetOrAddString(type.Name)); + @namespace: (ns == null) ? default : _metadataBuilder.GetOrAddString(ns), + name: _metadataBuilder.GetOrAddString(name)); private MemberReferenceHandle AddMemberReference(string memberName, EntityHandle parent, BlobBuilder signature) => _metadataBuilder.AddMemberReference( @@ -1094,8 +1092,21 @@ private EntityHandle GetHandleForMember(MemberInfo member) return GetMemberReferenceHandle(member); } - private static bool IsConstructedFromTypeBuilder(Type type) => type.IsConstructedGenericType && - (type.GetGenericTypeDefinition() is TypeBuilderImpl || ContainsTypeBuilder(type.GetGenericArguments())); + private static bool IsConstructedFromTypeBuilder(Type type) + { + if (type.IsConstructedGenericType) + { + return type.GetGenericTypeDefinition() is TypeBuilderImpl || ContainsTypeBuilder(type.GetGenericArguments()); + } + + Type? elementType = type.GetElementType(); + if (elementType is not null) + { + return (elementType is TypeBuilderImpl) || IsConstructedFromTypeBuilder(elementType); + } + + return false; + } internal static bool ContainsTypeBuilder(Type[] genericArguments) { diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs index faded2adaa8b3..90abf0d7342bf 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -2532,5 +2532,171 @@ public void ConstructorOfGenericTypeReferencedCorrectly() tlc.Unload(); } } + + [Fact] + public void TypeBuilderArrayReferencedInIL() + { + using (TempFile file = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder); + typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + MethodBuilder mb = typeBuilder.DefineMethod("Break", MethodAttributes.Static | MethodAttributes.Public, typeof(bool), [typeof(object)]); + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Isinst, typeBuilder.MakeArrayType()); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Ret); + + mb = typeBuilder.DefineMethod("MultiDimensionalArray", MethodAttributes.Static | MethodAttributes.Public, typeof(bool), [typeof(object)]); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Isinst, typeBuilder.MakeArrayType(3)); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Ret); + + mb = typeBuilder.DefineMethod("JaggedArray", MethodAttributes.Static | MethodAttributes.Public, typeof(Type), null); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, typeBuilder.MakeArrayType().MakeArrayType().MakeArrayType()); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")!); + il.Emit(OpCodes.Ret); + + typeBuilder.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("Break")!; + Assert.False((bool)method.Invoke(null, [typeFromDisk])); + object arrInst = Array.CreateInstance(typeFromDisk, 2)!; + Assert.True((bool)method.Invoke(null, [arrInst])); + + method = typeFromDisk.GetMethod("MultiDimensionalArray")!; + Assert.False((bool)method.Invoke(null, [arrInst])); + arrInst = Array.CreateInstance(typeFromDisk, 3, 2, 1)!; + Assert.True((bool)method.Invoke(null, [arrInst])); + + method = typeFromDisk.GetMethod("JaggedArray")!; + Type result = (Type)method.Invoke(null, null); + Assert.True(result.IsArray); + Assert.Equal("MyType[][][]", result.Name); + tlc.Unload(); + } + } + + [Fact] + public void GenericTypeParameterOfTypeBuilderArrayInIL() + { + using (TempFile file = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder); + typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + MethodInfo typeGetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle")!; + MethodBuilder mb = typeBuilder.DefineMethod("LoadDictionary", MethodAttributes.Static | MethodAttributes.Public, typeof(Type), null); + Type dictType = typeof(Dictionary<,>).MakeGenericType(typeBuilder.MakeArrayType(), typeof(int)); + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, dictType); + il.Emit(OpCodes.Call, typeGetTypeFromHandleMethod); + il.Emit(OpCodes.Ret); + + MethodBuilder mb2 = typeBuilder.DefineMethod("LoadList", MethodAttributes.Static | MethodAttributes.Public, typeof(Type), null); + Type listType = typeof(List<>).MakeGenericType(typeBuilder.MakeArrayType()); + il = mb2.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, listType.MakeArrayType()); + il.Emit(OpCodes.Call, typeGetTypeFromHandleMethod); + il.Emit(OpCodes.Ret); + typeBuilder.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("LoadDictionary")!; + Type result = (Type)method.Invoke(null, null); + Assert.Equal("Dictionary`2", result.Name); + Assert.True(result.IsConstructedGenericType); + Assert.Equal("MyType[]", result.GetGenericArguments()[0].Name); + method = typeFromDisk.GetMethod("LoadList")!; + result = (Type)method.Invoke(null, null); + Assert.Equal("List`1[]", result.Name); + Assert.True(result.IsArray); + result = result.GetElementType(); + Assert.Equal("MyType[]", result.GetGenericArguments()[0].Name); + tlc.Unload(); + } + } + + [Fact] + public void TypeBuilderPointerReferencedInIL() + { + using (TempFile file = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder); + typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + MethodBuilder mb = typeBuilder.DefineMethod("IsPointer", MethodAttributes.Static | MethodAttributes.Public, typeof(Type), null); + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, typeBuilder.MakePointerType()); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")!); + il.Emit(OpCodes.Ret); + typeBuilder.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("IsPointer")!; + Type result = (Type)method.Invoke(null, null); + Assert.Equal("MyType*", result.Name); + tlc.Unload(); + } + } + + [Fact] + public void TypeBuilderByRefReferencedInIL() + { + using (TempFile file = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder); + typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + MethodBuilder mb = typeBuilder.DefineMethod("GetByRef", MethodAttributes.Static | MethodAttributes.Public, typeof(Type), null); + ILGenerator il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, typeBuilder.MakeByRefType()); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")!); + il.Emit(OpCodes.Ret); + typeBuilder.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("GetByRef")!; + Type result = (Type)method.Invoke(null, null); + Assert.Equal("MyType&", result.Name); + tlc.Unload(); + } + } + + [Fact] + public void NestedTypeTokenReferencedCorrectlyInIL() + { + using (TempFile file = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder); + typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); + MethodBuilder writeMethod = typeBuilder.DefineMethod("Test", MethodAttributes.Public | MethodAttributes.Static); + ILGenerator il = writeMethod.GetILGenerator(); + il.DeclareLocal(typeof(HashSet.Enumerator)); + il.Emit(OpCodes.Newobj, typeof(HashSet).GetConstructors().First(c => c.GetParameters().Length == 0)); + il.Emit(OpCodes.Callvirt, typeof(HashSet).GetMethod("GetEnumerator")!); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ret); + typeBuilder.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("Test")!; + method.Invoke(null, null); // just make sure token works + tlc.Unload(); + } + } } } diff --git a/src/libraries/System.Reflection.Metadata/src/PACKAGE.md b/src/libraries/System.Reflection.Metadata/src/PACKAGE.md index f5891b8d74a4c..9b43fe952fef8 100644 --- a/src/libraries/System.Reflection.Metadata/src/PACKAGE.md +++ b/src/libraries/System.Reflection.Metadata/src/PACKAGE.md @@ -93,11 +93,11 @@ The main types provided by this library are: -* [System.Reflection.Metadata.MetadataReader](https://docs.microsoft.com/dotnet/api/system.reflection.metadata.metadatareader) -* [System.Reflection.PortableExecutable.PEReader](https://docs.microsoft.com/dotnet/api/system.reflection.portableexecutable.pereader) -* [System.Reflection.Metadata.Ecma335.MetadataBuilder](https://docs.microsoft.com/dotnet/api/system.reflection.metadata.ecma335.metadatabuilder) -* [System.Reflection.PortableExecutable.PEBuilder](https://docs.microsoft.com/dotnet/api/system.reflection.portableexecutable.pebuilder) -* [System.Reflection.PortableExecutable.ManagedPEBuilder](https://docs.microsoft.com/dotnet/api/system.reflection.portableexecutable.managedpebuilder) +* [System.Reflection.Metadata.MetadataReader](https://learn.microsoft.com/dotnet/api/system.reflection.metadata.metadatareader) +* [System.Reflection.PortableExecutable.PEReader](https://learn.microsoft.com/dotnet/api/system.reflection.portableexecutable.pereader) +* [System.Reflection.Metadata.Ecma335.MetadataBuilder](https://learn.microsoft.com/dotnet/api/system.reflection.metadata.ecma335.metadatabuilder) +* [System.Reflection.PortableExecutable.PEBuilder](https://learn.microsoft.com/dotnet/api/system.reflection.portableexecutable.pebuilder) +* [System.Reflection.PortableExecutable.ManagedPEBuilder](https://learn.microsoft.com/dotnet/api/system.reflection.portableexecutable.managedpebuilder) ## Feedback & Contributing diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/PACKAGE.md b/src/libraries/System.Reflection.MetadataLoadContext/src/PACKAGE.md index f16805d7d3f14..4c7566f5e7a9e 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/PACKAGE.md +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/PACKAGE.md @@ -50,9 +50,9 @@ The main types provided by this library are: -* [How to: Inspect assembly contents using MetadataLoadContext](https://docs.microsoft.com/dotnet/standard/assembly/inspect-contents-using-metadataloadcontext) -* [System.Reflection.MetadataLoadContext](https://docs.microsoft.com/dotnet/api/system.reflection.metadataloadcontext) -* [System.Reflection.MetadataAssemblyResolver](https://docs.microsoft.com/dotnet/api/system.reflection.metadataassemblyresolver) +* [How to: Inspect assembly contents using MetadataLoadContext](https://learn.microsoft.com/dotnet/standard/assembly/inspect-contents-using-metadataloadcontext) +* [System.Reflection.MetadataLoadContext](https://learn.microsoft.com/dotnet/api/system.reflection.metadataloadcontext) +* [System.Reflection.MetadataAssemblyResolver](https://learn.microsoft.com/dotnet/api/system.reflection.metadataassemblyresolver) ## Feedback & Contributing diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassGenerator.cs index 4f018470dab9a..918643a6ff689 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassGenerator.cs @@ -16,7 +16,6 @@ namespace Microsoft.Interop [Generator] public class ComClassGenerator : IIncrementalGenerator { - private sealed record ComClassInfo(string ClassName, ContainingSyntaxContext ContainingSyntaxContext, ContainingSyntax ClassSyntax, SequenceEqualImmutableArray ImplementedInterfacesNames); public void Initialize(IncrementalGeneratorInitializationContext context) { var unsafeCodeIsEnabled = context.CompilationProvider.Select((comp, ct) => comp.Options is CSharpCompilationOptions { AllowUnsafe: true }); // Unsafe code enabled @@ -27,54 +26,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context) static (node, ct) => node is ClassDeclarationSyntax, static (context, ct) => context) .Combine(unsafeCodeIsEnabled) - .Select((data, ct) => + .Select(static (data, ct) => { var context = data.Left; var unsafeCodeIsEnabled = data.Right; var type = (INamedTypeSymbol)context.TargetSymbol; var syntax = (ClassDeclarationSyntax)context.TargetNode; - if (!unsafeCodeIsEnabled) - { - return DiagnosticOr.From(DiagnosticInfo.Create(GeneratorDiagnostics.RequiresAllowUnsafeBlocks, syntax.Identifier.GetLocation())); - } - - if (!syntax.IsInPartialContext(out _)) - { - return DiagnosticOr.From( - DiagnosticInfo.Create( - GeneratorDiagnostics.InvalidAttributedClassMissingPartialModifier, - syntax.Identifier.GetLocation(), - type.ToDisplayString())); - } - - ImmutableArray.Builder names = ImmutableArray.CreateBuilder(); - foreach (INamedTypeSymbol iface in type.AllInterfaces) - { - AttributeData? generatedComInterfaceAttribute = iface.GetAttributes().FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.GeneratedComInterfaceAttribute); - if (generatedComInterfaceAttribute is not null) - { - var attributeData = GeneratedComInterfaceCompilationData.GetDataFromAttribute(generatedComInterfaceAttribute); - if (attributeData.Options.HasFlag(ComInterfaceOptions.ManagedObjectWrapper)) - { - names.Add(iface.ToDisplayString()); - } - } - } - - if (names.Count == 0) - { - return DiagnosticOr.From(DiagnosticInfo.Create(GeneratorDiagnostics.ClassDoesNotImplementAnyGeneratedComInterface, - syntax.Identifier.GetLocation(), - type.ToDisplayString())); - } - - - return DiagnosticOr.From( - new ComClassInfo( - type.ToDisplayString(), - new ContainingSyntaxContext(syntax), - new ContainingSyntax(syntax.Modifiers, syntax.Kind(), syntax.Identifier, syntax.TypeParameterList), - new(names.ToImmutable()))); + return ComClassInfo.From(type, syntax, unsafeCodeIsEnabled); }); var attributedClasses = context.FilterAndReportDiagnostics(attributedClassesOrDiagnostics); diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassInfo.cs new file mode 100644 index 0000000000000..920409ced00f8 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComClassInfo.cs @@ -0,0 +1,84 @@ +// 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.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.Interop +{ + internal sealed record ComClassInfo + { + public string ClassName { get; init; } + public ContainingSyntaxContext ContainingSyntaxContext { get; init; } + public ContainingSyntax ClassSyntax { get; init; } + public SequenceEqualImmutableArray ImplementedInterfacesNames { get; init; } + + private ComClassInfo(string className, ContainingSyntaxContext containingSyntaxContext, ContainingSyntax classSyntax, SequenceEqualImmutableArray implementedInterfacesNames) + { + ClassName = className; + ContainingSyntaxContext = containingSyntaxContext; + ClassSyntax = classSyntax; + ImplementedInterfacesNames = implementedInterfacesNames; + } + + public static DiagnosticOr From(INamedTypeSymbol type, ClassDeclarationSyntax syntax, bool unsafeCodeIsEnabled) + { + if (!unsafeCodeIsEnabled) + { + return DiagnosticOr.From(DiagnosticInfo.Create(GeneratorDiagnostics.RequiresAllowUnsafeBlocks, syntax.Identifier.GetLocation())); + } + + if (!syntax.IsInPartialContext(out _)) + { + return DiagnosticOr.From( + DiagnosticInfo.Create( + GeneratorDiagnostics.InvalidAttributedClassMissingPartialModifier, + syntax.Identifier.GetLocation(), + type.ToDisplayString())); + } + + ImmutableArray.Builder names = ImmutableArray.CreateBuilder(); + foreach (INamedTypeSymbol iface in type.AllInterfaces) + { + AttributeData? generatedComInterfaceAttribute = iface.GetAttributes().FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.GeneratedComInterfaceAttribute); + if (generatedComInterfaceAttribute is not null) + { + var attributeData = GeneratedComInterfaceCompilationData.GetDataFromAttribute(generatedComInterfaceAttribute); + if (attributeData.Options.HasFlag(ComInterfaceOptions.ManagedObjectWrapper)) + { + names.Add(iface.ToDisplayString()); + } + } + } + + if (names.Count == 0) + { + return DiagnosticOr.From(DiagnosticInfo.Create(GeneratorDiagnostics.ClassDoesNotImplementAnyGeneratedComInterface, + syntax.Identifier.GetLocation(), + type.ToDisplayString())); + } + + return DiagnosticOr.From( + new ComClassInfo( + type.ToDisplayString(), + new ContainingSyntaxContext(syntax), + new ContainingSyntax(syntax.Modifiers, syntax.Kind(), syntax.Identifier, syntax.TypeParameterList), + new(names.ToImmutable()))); + } + + public bool Equals(ComClassInfo? other) + { + return other is not null + && ClassName == other.ClassName + && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext) + && ImplementedInterfacesNames.SequenceEqual(other.ImplementedInterfacesNames); + } + + public override int GetHashCode() + { + return HashCode.Combine(ClassName, ContainingSyntaxContext, ImplementedInterfacesNames); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs index 0fc2d26cd106b..65bd2027f5d9b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceContext.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -9,8 +10,19 @@ namespace Microsoft.Interop { - internal sealed record ComInterfaceContext(ComInterfaceInfo Info, ComInterfaceContext? Base, ComInterfaceOptions Options) + internal sealed record ComInterfaceContext { + internal ComInterfaceInfo Info { get; init; } + internal ComInterfaceContext? Base { get; init; } + internal ComInterfaceOptions Options { get; init; } + + private ComInterfaceContext(ComInterfaceInfo info, ComInterfaceContext? @base, ComInterfaceOptions options) + { + Info = info; + Base = @base; + Options = options; + } + /// /// Takes a list of ComInterfaceInfo, and creates a list of ComInterfaceContext. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs index 0aed46284aa53..f0b4d8a452293 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceInfo.cs @@ -14,17 +14,40 @@ namespace Microsoft.Interop /// /// Information about a Com interface, but not its methods. /// - internal sealed record ComInterfaceInfo( - ManagedTypeInfo Type, - string ThisInterfaceKey, // For associating interfaces to its base - string? BaseInterfaceKey, // For associating interfaces to its base - InterfaceDeclarationSyntax Declaration, - ContainingSyntaxContext TypeDefinitionContext, - ContainingSyntax ContainingSyntax, - Guid InterfaceId, - ComInterfaceOptions Options, - Location DiagnosticLocation) + internal sealed record ComInterfaceInfo { + public ManagedTypeInfo Type { get; init; } + public string ThisInterfaceKey { get; init; } + public string? BaseInterfaceKey { get; init; } + public InterfaceDeclarationSyntax Declaration { get; init; } + public ContainingSyntaxContext TypeDefinitionContext { get; init; } + public ContainingSyntax ContainingSyntax { get; init; } + public Guid InterfaceId { get; init; } + public ComInterfaceOptions Options { get; init; } + public Location DiagnosticLocation { get; init; } + + private ComInterfaceInfo( + ManagedTypeInfo type, + string thisInterfaceKey, + string? baseInterfaceKey, + InterfaceDeclarationSyntax declaration, + ContainingSyntaxContext typeDefinitionContext, + ContainingSyntax containingSyntax, + Guid interfaceId, + ComInterfaceOptions options, + Location diagnosticLocation) + { + Type = type; + ThisInterfaceKey = thisInterfaceKey; + BaseInterfaceKey = baseInterfaceKey; + Declaration = declaration; + TypeDefinitionContext = typeDefinitionContext; + ContainingSyntax = containingSyntax; + InterfaceId = interfaceId; + Options = options; + DiagnosticLocation = diagnosticLocation; + } + public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceDeclarationSyntax syntax, StubEnvironment env, CancellationToken _) { if (env.Compilation.Options is not CSharpCompilationOptions { AllowUnsafe: true }) // Unsafe code enabled diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs index 39377c5cd3b44..6308ccae5cf46 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComMethodInfo.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -14,12 +15,25 @@ namespace Microsoft.Interop /// /// Represents a method that has been determined to be a COM interface method. Only contains info immediately available from an IMethodSymbol and MethodDeclarationSyntax. /// - internal sealed record ComMethodInfo( - MethodDeclarationSyntax Syntax, - string MethodName, - SequenceEqualImmutableArray Attributes, - bool IsUserDefinedShadowingMethod) + internal sealed record ComMethodInfo { + public MethodDeclarationSyntax Syntax { get; init; } + public string MethodName { get; init; } + public SequenceEqualImmutableArray Attributes { get; init; } + public bool IsUserDefinedShadowingMethod { get; init; } + + private ComMethodInfo( + MethodDeclarationSyntax syntax, + string methodName, + SequenceEqualImmutableArray attributes, + bool isUserDefinedShadowingMethod) + { + Syntax = syntax; + MethodName = methodName; + Attributes = attributes; + IsUserDefinedShadowingMethod = isUserDefinedShadowingMethod; + } + /// /// Returns a list of tuples of ComMethodInfo, IMethodSymbol, and Diagnostic. If ComMethodInfo is null, Diagnostic will not be null, and vice versa. /// @@ -95,7 +109,6 @@ internal sealed record ComMethodInfo( return DiagnosticOr<(ComMethodInfo, IMethodSymbol)>.From(DiagnosticInfo.Create(GeneratorDiagnostics.MethodNotDeclaredInAttributedInterface, method.Locations.FirstOrDefault(), method.ToDisplayString())); } - // Find the matching declaration syntax MethodDeclarationSyntax? comMethodDeclaringSyntax = null; foreach (var declaringSyntaxReference in method.DeclaringSyntaxReferences) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs index 366e13f61c524..caa9df8aa325d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs @@ -80,7 +80,11 @@ public static ManagedTypeInfo CreateTypeInfoForTypeSymbol(ITypeSymbol type) public sealed record SpecialTypeInfo(string FullTypeName, string DiagnosticFormattedName, SpecialType SpecialType) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName) { public static readonly SpecialTypeInfo Byte = new("byte", "byte", SpecialType.System_Byte); + public static readonly SpecialTypeInfo SByte = new("sbyte", "sbyte", SpecialType.System_SByte); + public static readonly SpecialTypeInfo Int16 = new("short", "short", SpecialType.System_Int16); + public static readonly SpecialTypeInfo UInt16 = new("ushort", "ushort", SpecialType.System_UInt16); public static readonly SpecialTypeInfo Int32 = new("int", "int", SpecialType.System_Int32); + public static readonly SpecialTypeInfo UInt32 = new("uint", "uint", SpecialType.System_UInt32); public static readonly SpecialTypeInfo Void = new("void", "void", SpecialType.System_Void); public static readonly SpecialTypeInfo String = new("string", "string", SpecialType.System_String); public static readonly SpecialTypeInfo Boolean = new("bool", "bool", SpecialType.System_Boolean); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshalAsParser.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshalAsParser.cs index 2ccde8160d7b7..2d42379bea06b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshalAsParser.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshalAsParser.cs @@ -21,7 +21,7 @@ public abstract record MarshalAsInfo( CharEncoding CharEncoding) : MarshallingInfoStringSupport(CharEncoding), IForwardedMarshallingInfo { // UnmanagedType.LPUTF8Str is not in netstandard2.0, so we define a constant for the value here. - // See https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype + // See https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype internal const UnmanagedType UnmanagedType_LPUTF8Str = (UnmanagedType)0x30; private protected abstract bool TryCreateAttributeSyntax([NotNullWhen(true)] out AttributeSyntax? attribute); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorResolver.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorResolver.cs index 3aeb0c282dc24..9a6c6c82d3469 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorResolver.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorResolver.cs @@ -340,7 +340,7 @@ private ResolvedGenerator CreateNativeCollectionMarshaller( marshallerType = marshallerType with { FullTypeName = marshallerTypeSyntax.ToString(), - DiagnosticFormattedName = marshallerTypeSyntax.ToString(), + DiagnosticFormattedName = marshallerTypeSyntax.ToString() }; string newNativeTypeName = ReplacePlaceholderSyntaxWithUnmanagedTypeSyntax(marshallerData.NativeType.Syntax, marshalInfo, unmanagedElementType).ToFullString(); ManagedTypeInfo nativeType = marshallerData.NativeType with diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs index a310f9dd5c120..24e2102eb7399 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs @@ -28,7 +28,7 @@ protected BoolMarshallerBase(ManagedTypeInfo nativeType, int trueValue, int fals public ManagedTypeInfo AsNativeType(TypePositionInfo info) { - Debug.Assert(info.ManagedType is SpecialTypeInfo(_, _, SpecialType.System_Boolean)); + Debug.Assert(info.ManagedType is SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }); return _nativeType; } @@ -118,7 +118,7 @@ public sealed class ByteBoolMarshaller : BoolMarshallerBase /// /// True if the byte should be signed, otherwise false public ByteBoolMarshaller(bool signed) - : base(new SpecialTypeInfo(signed ? "sbyte" : "byte", signed ? "sbyte" : "byte", signed ? SpecialType.System_SByte : SpecialType.System_Byte), trueValue: 1, falseValue: 0, compareToTrue: false) + : base(signed ? SpecialTypeInfo.SByte : SpecialTypeInfo.Byte, trueValue: 1, falseValue: 0, compareToTrue: false) { } } @@ -127,7 +127,7 @@ public ByteBoolMarshaller(bool signed) /// Marshals a boolean value as a 4-byte integer. /// /// - /// Corresponds to the definition of BOOL. + /// Corresponds to the definition of BOOL. /// public sealed class WinBoolMarshaller : BoolMarshallerBase { @@ -136,7 +136,7 @@ public sealed class WinBoolMarshaller : BoolMarshallerBase /// /// True if the int should be signed, otherwise false public WinBoolMarshaller(bool signed) - : base(new SpecialTypeInfo(signed ? "int" : "uint", signed ? "int" : "uint", signed ? SpecialType.System_Int32 : SpecialType.System_UInt32), trueValue: 1, falseValue: 0, compareToTrue: false) + : base(signed ? SpecialTypeInfo.Int32 : SpecialTypeInfo.UInt32, trueValue: 1, falseValue: 0, compareToTrue: false) { } } @@ -149,7 +149,7 @@ public sealed class VariantBoolMarshaller : BoolMarshallerBase private const short VARIANT_TRUE = -1; private const short VARIANT_FALSE = 0; public VariantBoolMarshaller() - : base(new SpecialTypeInfo("short", "short", SpecialType.System_Int16), trueValue: VARIANT_TRUE, falseValue: VARIANT_FALSE, compareToTrue: true) + : base(SpecialTypeInfo.Int16, trueValue: VARIANT_TRUE, falseValue: VARIANT_FALSE, compareToTrue: true) { } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs index 90aea0de280c3..02e73d70b011e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs @@ -23,7 +23,7 @@ public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) } // Breaking change: [MarshalAs(UnmanagedType.Struct)] in object in unmanaged-to-managed scenarios will not respect VT_BYREF. - if (info is { RefKind: RefKind.In or RefKind.RefReadOnlyParameter, MarshallingAttributeInfo: NativeMarshallingAttributeInfo(ManagedTypeInfo(_, TypeNames.ComVariantMarshaller), _) } + if (info is { RefKind: RefKind.In or RefKind.RefReadOnlyParameter, MarshallingAttributeInfo: NativeMarshallingAttributeInfo(ManagedTypeInfo { DiagnosticFormattedName: TypeNames.ComVariantMarshaller }, _) } && context.Direction == MarshalDirection.UnmanagedToManaged) { gen = ResolvedGenerator.ResolvedWithDiagnostics( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs index e02bf7826a85d..dc70548ff4f06 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs @@ -13,7 +13,7 @@ namespace Microsoft.Interop { public sealed class Utf16CharMarshaller : IMarshallingGenerator { - private static readonly ManagedTypeInfo s_nativeType = new SpecialTypeInfo("ushort", "ushort", SpecialType.System_UInt16); + private static readonly ManagedTypeInfo s_nativeType = SpecialTypeInfo.UInt16; public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) { @@ -35,7 +35,7 @@ public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, Stu public ManagedTypeInfo AsNativeType(TypePositionInfo info) { - Debug.Assert(info.ManagedType is SpecialTypeInfo(_, _, SpecialType.System_Char)); + Debug.Assert(info.ManagedType is SpecialTypeInfo {SpecialType: SpecialType.System_Char }); return s_nativeType; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorResolver.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorResolver.cs index f8c1efdb71860..05d9b5ab31c1a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorResolver.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorResolver.cs @@ -64,11 +64,11 @@ public ResolvedGenerator Create( return ResolvedGenerator.Resolved(s_blittable); // Pointer with no marshalling info - case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: false), MarshallingAttributeInfo: NoMarshallingInfo }: + case { ManagedType: PointerTypeInfo{ IsFunctionPointer: false }, MarshallingAttributeInfo: NoMarshallingInfo }: return ResolvedGenerator.Resolved(s_blittable); // Function pointer with no marshalling info - case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: true), MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: + case { ManagedType: PointerTypeInfo { IsFunctionPointer: true }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: return ResolvedGenerator.Resolved(s_blittable); // Bool with marshalling info diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 91028ff5a6ebd..25e09c52a6008 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -426,6 +426,11 @@ public static SyntaxTokenList GetManagedParameterModifiers(TypePositionInfo type } } + if (typeInfo.IsExplicitThis) + { + tokens = tokens.Add(Token(SyntaxKind.ThisKeyword)); + } + return tokens; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs index c382262bd47b4..8177de0b1fdbd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs @@ -148,13 +148,13 @@ public sealed record ComExceptionMarshalling : MarshallingInfo { internal static MarshallingInfo CreateSpecificMarshallingInfo(ManagedTypeInfo unmanagedReturnType) { - return unmanagedReturnType switch + return (unmanagedReturnType as SpecialTypeInfo)?.SpecialType switch { - SpecialTypeInfo(_, _, SpecialType.System_Void) => CreateWellKnownComExceptionMarshallingData(TypeNames.ExceptionAsVoidMarshaller, unmanagedReturnType), - SpecialTypeInfo(_, _, SpecialType.System_Int32) => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsHResultMarshaller}", unmanagedReturnType), - SpecialTypeInfo(_, _, SpecialType.System_UInt32) => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsHResultMarshaller}", unmanagedReturnType), - SpecialTypeInfo(_, _, SpecialType.System_Single) => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsNaNMarshaller}", unmanagedReturnType), - SpecialTypeInfo(_, _, SpecialType.System_Double) => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsNaNMarshaller}", unmanagedReturnType), + SpecialType.System_Void => CreateWellKnownComExceptionMarshallingData(TypeNames.ExceptionAsVoidMarshaller, unmanagedReturnType), + SpecialType.System_Int32 => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsHResultMarshaller}", unmanagedReturnType), + SpecialType.System_UInt32 => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsHResultMarshaller}", unmanagedReturnType), + SpecialType.System_Single => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsNaNMarshaller}", unmanagedReturnType), + SpecialType.System_Double => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsNaNMarshaller}", unmanagedReturnType), _ => CreateWellKnownComExceptionMarshallingData($"{TypeNames.ExceptionAsDefaultMarshaller}<{MarshallerHelpers.GetCompatibleGenericTypeParameterSyntax(SyntaxFactory.ParseTypeName(unmanagedReturnType.FullTypeName))}>", unmanagedReturnType), }; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index 49ebf92f600ec..69654c34c4f75 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -3,9 +3,10 @@ using System; using System.Collections.Generic; - +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Interop @@ -77,6 +78,7 @@ public static int IncrementIndex(int index) public int ManagedIndex { get; init; } = UnsetIndex; public int NativeIndex { get; init; } = UnsetIndex; + public bool IsExplicitThis { get; init; } public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, MarshallingInfo marshallingInfo, Compilation compilation) { @@ -88,7 +90,8 @@ public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, RefKind = paramSymbol.RefKind, ByValueContentsMarshalKind = byValueContentsMarshalKind, ByValueMarshalAttributeLocations = (inLocation, outLocation), - ScopedKind = paramSymbol.ScopedKind + ScopedKind = paramSymbol.ScopedKind, + IsExplicitThis = ((ParameterSyntax)paramSymbol.DeclaringSyntaxReferences[0].GetSyntax()).Modifiers.Any(SyntaxKind.ThisKeyword) }; return typeInfo; diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 5c47f26a0177d..4a29736a8652b 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -674,7 +674,9 @@ public static partial class CollectionsMarshal { public static System.Span AsSpan(System.Collections.Generic.List? list) { throw null; } public static ref TValue GetValueRefOrNullRef(System.Collections.Generic.Dictionary dictionary, TKey key) where TKey : notnull { throw null; } + public static ref TValue GetValueRefOrNullRef(System.Collections.Generic.Dictionary.AlternateLookup dictionary, TAlternateKey key) where TKey : notnull where TAlternateKey : notnull, allows ref struct { throw null; } public static ref TValue? GetValueRefOrAddDefault(System.Collections.Generic.Dictionary dictionary, TKey key, out bool exists) where TKey : notnull { throw null; } + public static ref TValue? GetValueRefOrAddDefault(System.Collections.Generic.Dictionary.AlternateLookup dictionary, TAlternateKey key, out bool exists) where TKey : notnull where TAlternateKey : notnull, allows ref struct { throw null; } public static void SetCount(System.Collections.Generic.List list, int count) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class, Inherited=false)] diff --git a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml index b4c6a06271867..da3c19b381045 100644 --- a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComTypes/ADVF.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComTypes/ADVF.cs index f8a44d0a9e86b..8caac60ad38dc 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComTypes/ADVF.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComTypes/ADVF.cs @@ -8,7 +8,7 @@ namespace System.Runtime.InteropServices.ComTypes /// /// Note: ADVF_ONLYONCE and ADVF_PRIMEFIRST values conform with objidl.dll but are backwards from /// the Platform SDK documentation as of 07/21/2003. - /// https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ne-objidl-tagadvf. + /// https://learn.microsoft.com/windows/desktop/api/objidl/ne-objidl-tagadvf. /// [EditorBrowsable(EditorBrowsableState.Never)] [Flags] diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs index e082fc4b25417..c0d7398b039c1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs @@ -115,6 +115,73 @@ public static partial BoolStruct[] NegateBools( } } + public static partial class ArrayNativeExtensions + { + // The first parameter of a 'ref' extension method must be a value type or a generic type constrained to struct. + // The first 'in' or 'ref readonly' parameter of the extension method must be a concrete (non-generic) value type. + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "sum_int_array")] + public static partial int Sum(this int[] values, int numValues); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "sum_int_array")] + public static partial int Sum(this ref int values, int numValues); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "sum_char_array", StringMarshalling = StringMarshalling.Utf16)] + public static partial int SumChars(this char[] chars, int numElements); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "fill_char_array", StringMarshalling = StringMarshalling.Utf16)] + public static partial void FillChars([Out] this char[] chars, int length, ushort start); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")] + public static partial int SumStringLengths([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] this string[] strArray); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "reverse_strings_return")] + [return: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)] + public static partial string[] ReverseStrings_Return([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] this string[] strArray, out int numElements); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "reverse_strings_out")] + public static partial void ReverseStrings_Out([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] this string[] strArray, out int numElements, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)] out string[] res); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "get_long_bytes")] + [return: MarshalAs(UnmanagedType.LPArray, SizeConst = sizeof(long))] + public static partial byte[] GetLongBytes(this long l); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "fill_range_array")] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool FillRangeArray([Out] this IntStructWrapper[] array, int length, int start); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "double_values")] + public static partial void DoubleValues([In, Out] this IntStructWrapper[] array, int length); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "and_bool_struct_array")] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool AndAllMembers(this BoolStruct[] pArray, int length); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "negate_bool_struct_array_out")] + public static partial void NegateBools( + this BoolStruct[] boolStruct, + int numValues, + [MarshalUsing(CountElementName = "numValues")] out BoolStruct[] pBoolStructOut); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "negate_bool_struct_array_return")] + [return: MarshalUsing(CountElementName = "numValues")] + public static partial BoolStruct[] NegateBools( + this BoolStruct[] boolStruct, + int numValues); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "transpose_matrix")] + [return: MarshalUsing(CountElementName = "numColumns")] + [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionDepth = 1)] + public static partial int[][] TransposeMatrix(this int[][] matrix, int[] numRows, int numColumns); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")] + public static unsafe partial int Sum(this int*[] values, int numValues); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "return_duplicate_int_ptr_array")] + [return: MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + public static unsafe partial int*[] ReturnDuplicate(this int*[] values, int numValues); + } + public class ArrayTests { private int[] GetIntArray() => new[] { 1, 5, 79, 165, 32, 3 }; @@ -126,6 +193,13 @@ public void IntArray_ByValue() Assert.Equal(array.Sum(), NativeExportsNE.Arrays.Sum(array, array.Length)); } + [Fact] + public void IntArray_ByValue_This() + { + int[] array = GetIntArray(); + Assert.Equal(array.Sum(), array.Sum(array.Length)); + } + [Fact] public void IntArray_RefToFirstElement() { @@ -133,6 +207,13 @@ public void IntArray_RefToFirstElement() Assert.Equal(array.Sum(), NativeExportsNE.Arrays.Sum(ref array[0], array.Length)); } + [Fact] + public void IntArray_RefToFirstElement_This() + { + int[] array = GetIntArray(); + Assert.Equal(array.Sum(), array[0].Sum(array.Length)); + } + [Fact] public void NullIntArray_ByValue() { @@ -147,6 +228,13 @@ public void ZeroLengthArray_MarshalledAsNonNull() Assert.Equal(0, NativeExportsNE.Arrays.Sum(array, array.Length)); } + [Fact] + public void ZeroLengthArray_MarshalledAsNonNull_This() + { + var array = new int[0]; + Assert.Equal(0, array.Sum(array.Length)); + } + [Fact] public void IntArray_In() { @@ -170,6 +258,13 @@ public void CharArray_ByValue() Assert.Equal(array.Sum(c => c), NativeExportsNE.Arrays.SumChars(array, array.Length)); } + [Fact] + public void CharArray_ByValue_This() + { + char[] array = CharacterTests.CharacterMappings().Select(o => (char)o[0]).ToArray(); + Assert.Equal(array.Sum(c => c), array.SumChars(array.Length)); + } + [Fact] public void CharArray_Ref() { @@ -219,6 +314,22 @@ public unsafe void PointerArray_ByValue() } } + [Fact] + public unsafe void PointerArray_ByValue_This() + { + int[] array = GetIntArray(); + fixed (int* arrayPointer = array) + { + int*[] pointerArray = new int*[array.Length]; + for (int i = 0; i < array.Length; i++) + { + pointerArray[i] = &arrayPointer[i]; + } + + Assert.Equal(array.Sum(), pointerArray.Sum(pointerArray.Length)); + } + } + [Fact] public unsafe void PointerArray_In() { @@ -281,6 +392,28 @@ public unsafe void PointerArray_Return() } } + [Fact] + public unsafe void PointerArray_Return_This() + { + int[] array = GetIntArray(); + fixed (int* arrayPointer = array) + { + int*[] pointerArray = new int*[array.Length]; + for (int i = 0; i < array.Length; i++) + { + pointerArray[i] = &arrayPointer[i]; + } + + int*[] res = pointerArray.ReturnDuplicate(pointerArray.Length); + Assert.Equal(pointerArray.Length, res.Length); + for (int i = 0; i < pointerArray.Length; i++) + { + Assert.Equal((IntPtr)pointerArray[i], (IntPtr)res[i]); + Assert.Equal(*pointerArray[i], *res[i]); + } + } + } + private static string[] GetStringArray() { return new[] @@ -301,12 +434,26 @@ public void ArrayWithElementMarshalling_ByValue() Assert.Equal(strings.Sum(str => str?.Length ?? 0), NativeExportsNE.Arrays.SumStringLengths(strings)); } + [Fact] + public void ArrayWithElementMarshalling_ByValue_This() + { + var strings = GetStringArray(); + Assert.Equal(strings.Sum(str => str?.Length ?? 0), strings.SumStringLengths()); + } + [Fact] public void NullArrayWithElementMarshalling_ByValue() { Assert.Equal(0, NativeExportsNE.Arrays.SumStringLengths(null)); } + [Fact] + public void NullArrayWithElementMarshalling_ByValue_This() + { + string[] strings = null; + Assert.Equal(0, strings.SumStringLengths()); + } + [Fact] public void ArrayWithElementMarshalling_Ref() { @@ -329,6 +476,18 @@ public void ArrayWithElementMarshalling_Return() Assert.Equal(expectedStrings, res); } + [Fact] + public void ArrayWithElementMarshalling_Return_This() + { + var strings = GetStringArray(); + var expectedStrings = strings.Select(s => ReverseChars(s)).ToArray(); + Assert.Equal(expectedStrings, strings.ReverseStrings_Return(out _)); + + string[] res; + strings.ReverseStrings_Out(out _, out res); + Assert.Equal(expectedStrings, res); + } + [Fact] public void NullArrayWithElementMarshalling_Ref() { @@ -349,6 +508,17 @@ public void NullArrayWithElementMarshalling_Return() Assert.Null(res); } + [Fact] + public void NullArrayWithElementMarshalling_Return_This() + { + string[] strings = null; + Assert.Null(strings.ReverseStrings_Return(out _)); + + string[] res; + strings.ReverseStrings_Out(out _, out res); + Assert.Null(res); + } + [Fact] public void ConstantSizeArray() { @@ -357,6 +527,14 @@ public void ConstantSizeArray() Assert.Equal(longVal, MemoryMarshal.Read(NativeExportsNE.Arrays.GetLongBytes(longVal))); } + [Fact] + public void ConstantSizeArray_This() + { + var longVal = 0x12345678ABCDEF10L; + + Assert.Equal(longVal, MemoryMarshal.Read(longVal.GetLongBytes())); + } + [Fact] public void DynamicSizedArrayWithConstantComponent() { @@ -400,6 +578,39 @@ public void Array_ByValueOut() } } + [Fact] + public void Array_ByValueOut_This() + { + { + var testArray = new IntStructWrapper[10]; + int start = 5; + + testArray.FillRangeArray(testArray.Length, start); + Assert.Equal(Enumerable.Range(start, testArray.Length), testArray.Select(wrapper => wrapper.Value)); + + // Any items not populated by the invoke target should be initialized to default + testArray = new IntStructWrapper[10]; + int lengthToFill = testArray.Length / 2; + testArray.FillRangeArray(lengthToFill, start); + Assert.Equal(Enumerable.Range(start, lengthToFill), testArray[..lengthToFill].Select(wrapper => wrapper.Value)); + Assert.All(testArray[lengthToFill..], wrapper => Assert.Equal(0, wrapper.Value)); + } + { + var testArray = new char[10]; + ushort start = 65; + + testArray.FillChars(testArray.Length, start); + Assert.Equal(Enumerable.Range(start, testArray.Length), testArray.Select(c => (int)c)); + + // Any items not populated by the invoke target should be initialized to default + testArray = new char[10]; + int lengthToFill = testArray.Length / 2; + testArray.FillChars(lengthToFill, start); + Assert.Equal(Enumerable.Range(start, lengthToFill), testArray[..lengthToFill].Select(c => (int)c)); + Assert.All(testArray[lengthToFill..], c => Assert.Equal(0, c)); + } + } + [Fact] public void Array_ByValueInOut() { @@ -412,6 +623,18 @@ public void Array_ByValueInOut() Assert.Equal(testValues.Select(wrapper => wrapper.Value * 2), testArray.Select(wrapper => wrapper.Value)); } + [Fact] + public void Array_ByValueInOut_This() + { + var testValues = Enumerable.Range(42, 15).Select(i => new IntStructWrapper { Value = i }); + + var testArray = testValues.ToArray(); + + testArray.DoubleValues(testArray.Length); + + Assert.Equal(testValues.Select(wrapper => wrapper.Value * 2), testArray.Select(wrapper => wrapper.Value)); + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -421,6 +644,15 @@ public void NonBlittableElementArray_ByValue(bool result) Assert.Equal(result, NativeExportsNE.Arrays.AndAllMembers(array, array.Length)); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void NonBlittableElementArray_ByValue_This(bool result) + { + BoolStruct[] array = GetBoolStructsToAnd(result); + Assert.Equal(result, array.AndAllMembers(array.Length)); + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -451,6 +683,17 @@ public void NonBlittableElementArray_Out() Assert.Equal(expected, result); } + [Fact] + public void NonBlittableElementArray_Out_This() + { + BoolStruct[] array = GetBoolStructsToNegate(); + BoolStruct[] expected = GetNegatedBoolStructs(array); + + BoolStruct[] result; + array.NegateBools(array.Length, out result); + Assert.Equal(expected, result); + } + [Fact] public void NonBlittableElementArray_Return() { @@ -461,6 +704,16 @@ public void NonBlittableElementArray_Return() Assert.Equal(expected, result); } + [Fact] + public void NonBlittableElementArray_Return_This() + { + BoolStruct[] array = GetBoolStructsToNegate(); + BoolStruct[] expected = GetNegatedBoolStructs(array); + + BoolStruct[] result = array.NegateBools(array.Length); + Assert.Equal(expected, result); + } + private static BoolStruct[] GetBoolStructsToAnd(bool result) => new BoolStruct[] { new BoolStruct @@ -544,6 +797,36 @@ public void ArraysOfArrays() } } + [Fact] + public void ArraysOfArrays_This() + { + var random = new Random(42); + int numRows = random.Next(1, 5); + int numColumns = random.Next(1, 5); + int[][] matrix = new int[numRows][]; + for (int i = 0; i < numRows; i++) + { + matrix[i] = new int[numColumns]; + for (int j = 0; j < numColumns; j++) + { + matrix[i][j] = random.Next(); + } + } + + int[] numRowsArray = new int[numColumns]; + numRowsArray.AsSpan().Fill(numRows); + + int[][] transposed = matrix.TransposeMatrix(numRowsArray, numColumns); + + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < numColumns; j++) + { + Assert.Equal(matrix[i][j], transposed[j][i]); + } + } + } + private static string ReverseChars(string value) { if (value == null) diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/BlittableStructTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/BlittableStructTests.cs index f02379ddcabf2..c3a6ea8588837 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/BlittableStructTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/BlittableStructTests.cs @@ -41,6 +41,16 @@ public static partial void IncrementInvertPointerFieldsRefReturn( PointerFields input, ref PointerFields result); } + public static partial class IntStructExtensions + { + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "blittablestructs_return_instance")] + public static partial IntFields DoubleIntFields(this IntFields result); + + [LibraryImport(NativeExportsNE.NativeExportsNE_Binary, EntryPoint = "blittablestructs_double_intfields_refreturn")] + public static partial void DoubleIntFieldsOutReturn( + this IntFields input, + out IntFields result); + } public class BlittableStructTests { @@ -67,6 +77,11 @@ public void ValidateIntFields() Assert.Equal(initial, input); Assert.Equal(expected, result); } + { + var result = input.DoubleIntFields(); + Assert.Equal(initial, input); + Assert.Equal(expected, result); + } { var result = new IntFields(); NativeExportsNE.DoubleIntFieldsRefReturn(input, ref result); @@ -80,6 +95,12 @@ public void ValidateIntFields() Assert.Equal(initial, input); Assert.Equal(expected, result); } + { + IntFields result; + input.DoubleIntFieldsOutReturn(out result); + Assert.Equal(initial, input); + Assert.Equal(expected, result); + } { input = initial; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 32a0d9a414e19..6204b100ce325 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -532,6 +532,15 @@ partial class Test } """; + public static string ExplicitThis => $$""" + using System.Runtime.InteropServices; + static partial class StringNativeExtensions + { + [LibraryImport("DoesNotExist")] + public static partial void Method(this int t); + } + """; + public static string BasicParametersAndModifiers(string preDeclaration = "") => BasicParametersAndModifiers(typeof(T).ToString(), preDeclaration); /// 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 4c80ac61cc460..43fdaf4930037 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs @@ -42,6 +42,7 @@ public static IEnumerable CodeSnippetsToCompile() yield return new[] { ID(), CodeSnippets.DefaultParameters }; yield return new[] { ID(), CodeSnippets.UseCSharpFeaturesForConstants }; yield return new[] { ID(), CodeSnippets.LibraryImportInRefStruct }; + yield return new[] { ID(), CodeSnippets.ExplicitThis }; // Parameter / return types yield return new[] { ID(), CodeSnippets.BasicParametersAndModifiers() }; @@ -719,7 +720,7 @@ public class Basic { } [Theory] [MemberData(nameof(CodeSnippetsToVerifyNoTreesProduced))] - public async Task ValidateNoGeneratedOuptutForNoImport(string id, string source, TestTargetFramework framework) + public async Task ValidateNoGeneratedOutputForNoImport(string id, string source, TestTargetFramework framework) { TestUtils.Use(id); var test = new NoChangeTest(framework) diff --git a/src/libraries/System.Runtime.Intrinsics/README.md b/src/libraries/System.Runtime.Intrinsics/README.md index 467c9fe55d4e0..f5852a4655687 100644 --- a/src/libraries/System.Runtime.Intrinsics/README.md +++ b/src/libraries/System.Runtime.Intrinsics/README.md @@ -1,7 +1,7 @@ # System.Runtime.Intrinsics Contains types used to create and convey register states in various sizes and formats for use with instruction-set extensions. Also exposes select instruction-set extensions for various architectures (`x86`, `Arm`, `Wasm`). -Documentation can be found here: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics. +Documentation can be found here: https://learn.microsoft.com/dotnet/api/system.runtime.intrinsics. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) 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 64390b9502a94..7ed0b7d273a3f 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -4272,10 +4272,30 @@ internal Arm64() { } public static System.Numerics.Vector BooleanNot(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector BooleanNot(System.Numerics.Vector value) { throw null; } - public static ulong Count16BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } - public static ulong Count32BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } - public static ulong Count64BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } - public static ulong Count8BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + + public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute32BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute32BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute32BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute32BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute64BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute64BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute64BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute64BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -4286,6 +4306,12 @@ internal Arm64() { } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static ulong Count16BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong Count32BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong Count64BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong Count8BitElements([ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector CreateFalseMaskByte() { throw null; } public static System.Numerics.Vector CreateFalseMaskDouble() { throw null; } public static System.Numerics.Vector CreateFalseMaskInt16() { throw null; } @@ -4365,6 +4391,17 @@ internal Arm64() { } public static System.Numerics.Vector FusedMultiplySubtractNegated(System.Numerics.Vector minuend, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector FusedMultiplySubtractNegated(System.Numerics.Vector minuend, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static ulong GetActiveElementCount(System.Numerics.Vector mask, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector LeadingSignCount(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector LeadingSignCount(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector LeadingSignCount(System.Numerics.Vector value) { throw null; } @@ -4413,6 +4450,63 @@ internal Arm64() { } public static unsafe System.Numerics.Vector LoadVectorUInt32ZeroExtendToInt64(System.Numerics.Vector mask, uint* address) { throw null; } public static unsafe System.Numerics.Vector LoadVectorUInt32ZeroExtendToUInt64(System.Numerics.Vector mask, uint* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToInt16(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToInt32(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToInt64(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToUInt16(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToUInt32(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorByteNonFaultingZeroExtendToUInt64(byte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt16NonFaultingZeroExtendToInt32(ushort* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt16NonFaultingZeroExtendToInt64(ushort* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt16NonFaultingZeroExtendToUInt32(ushort* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt16NonFaultingZeroExtendToUInt64(ushort* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt32NonFaultingZeroExtendToInt64(uint* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorUInt32NonFaultingZeroExtendToUInt64(uint* address) { throw null; } + + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, byte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, double* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, short* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, int* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, long* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, sbyte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, float* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, ushort* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, uint* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector) Load2xVectorAndUnzip(System.Numerics.Vector mask, ulong* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, byte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, double* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, short* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, int* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, long* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, sbyte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, float* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, ushort* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, uint* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load3xVectorAndUnzip(System.Numerics.Vector mask, ulong* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, byte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, double* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, short* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, int* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, long* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, sbyte* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, float* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, ushort* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, uint* address) { throw null; } + public static unsafe (System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector, System.Numerics.Vector) Load4xVectorAndUnzip(System.Numerics.Vector mask, ulong* address) { throw null; } + + public static unsafe System.Numerics.Vector LoadVectorInt16NonFaultingSignExtendToInt32(short* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorInt16NonFaultingSignExtendToInt64(short* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorInt16NonFaultingSignExtendToUInt32(short* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorInt16NonFaultingSignExtendToUInt64(short* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorInt32NonFaultingSignExtendToInt64(int* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorInt32NonFaultingSignExtendToUInt64(int* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToInt16(sbyte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToInt32(sbyte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToInt64(sbyte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToUInt16(sbyte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToUInt32(sbyte* address) { throw null; } + public static unsafe System.Numerics.Vector LoadVectorSByteNonFaultingSignExtendToUInt64(sbyte* address) { throw null; } + public static System.Numerics.Vector Max(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Max(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Max(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -4533,6 +4627,121 @@ internal Arm64() { } public static System.Numerics.Vector PopCount(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector PopCount(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement16(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement16(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement16(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement16(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement32(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement32(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ReverseElement8(System.Numerics.Vector value) { throw null; } + + public static int SaturatingDecrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingDecrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingDecrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingDecrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy16BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy16BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingDecrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingDecrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingDecrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingDecrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy32BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy32BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingDecrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingDecrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingDecrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingDecrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy64BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingDecrementBy64BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingDecrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingDecrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingDecrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingDecrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + + public static long SaturatingDecrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingDecrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingDecrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingDecrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + + public static int SaturatingIncrementBy16BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingIncrementBy16BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingIncrementBy16BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingIncrementBy16BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy16BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy16BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingIncrementBy32BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingIncrementBy32BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingIncrementBy32BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingIncrementBy32BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy32BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy32BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingIncrementBy64BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingIncrementBy64BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingIncrementBy64BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingIncrementBy64BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy64BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static System.Numerics.Vector SaturatingIncrementBy64BitElementCount(System.Numerics.Vector value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static int SaturatingIncrementBy8BitElementCount(int value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static long SaturatingIncrementBy8BitElementCount(long value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static uint SaturatingIncrementBy8BitElementCount(uint value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + public static ulong SaturatingIncrementBy8BitElementCount(ulong value, [ConstantExpected(Min = 1, Max = (byte)(16))] byte scale, [ConstantExpected] SveMaskPattern pattern = SveMaskPattern.All) { throw null; } + + public static long SaturatingIncrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(int value, System.Numerics.Vector from) { throw null; } + public static long SaturatingIncrementByActiveElementCount(long value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(uint value, System.Numerics.Vector from) { throw null; } + public static ulong SaturatingIncrementByActiveElementCount(ulong value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SaturatingIncrementByActiveElementCount(System.Numerics.Vector value, System.Numerics.Vector from) { throw null; } + public static System.Numerics.Vector SignExtend16(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector SignExtend16(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector SignExtend32(System.Numerics.Vector value) { throw null; } @@ -4601,6 +4810,17 @@ internal Arm64() { } public static unsafe void StoreNarrowing(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector data) { throw null; } public static unsafe void StoreNarrowing(System.Numerics.Vector mask, uint* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, byte* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, double* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, short* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, int* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, long* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, sbyte* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, float* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, uint* address, System.Numerics.Vector data) { throw null; } + public static unsafe void StoreNonTemporal(System.Numerics.Vector mask, ulong* address, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector Subtract(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Subtract(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector Subtract(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -4621,6 +4841,27 @@ internal Arm64() { } public static System.Numerics.Vector SubtractSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector SubtractSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector UnzipEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector UnzipEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector UnzipEven(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } diff --git a/src/libraries/System.Runtime.Numerics/README.md b/src/libraries/System.Runtime.Numerics/README.md index 2aa9e24c31122..8ef5e0f375d31 100644 --- a/src/libraries/System.Runtime.Numerics/README.md +++ b/src/libraries/System.Runtime.Numerics/README.md @@ -5,9 +5,9 @@ Contains additional numeric types that complement the numeric primitives (such a * The `Complex` structure, which represents a complex number. A complex number is a number in the form *a* + *b*i, where *a* is the real part, and *b* is the imaginary part. * The SIMD-enabled vector types, which include `Vector2`, `Vector3`, `Vector4`, `Matrix3x2`, `Matrix4x4`, `Plane`, and `Quaternion`. -Documentation can be found here: https://learn.microsoft.com/en-us/dotnet/api/system.numerics. +Documentation can be found here: https://learn.microsoft.com/dotnet/api/system.numerics. -This area also includes all of the interfaces that make up Generic Math, which is discussed more here: https://learn.microsoft.com/en-us/dotnet/standard/generics/math. +This area also includes all of the interfaces that make up Generic Math, which is discussed more here: https://learn.microsoft.com/dotnet/standard/generics/math. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 2ca6c7a1bf5ab..59fef1a6d5da6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -27,6 +27,15 @@ public readonly struct BigInteger internal const int kcbitUlong = 64; internal const int DecimalScaleFactorMask = 0x00FF0000; + // Various APIs only allow up to int.MaxValue bits, so we will restrict ourselves + // to fit within this given our underlying storage representation and the maximum + // array length. This gives us just shy of 256MB as the largest allocation size. + // + // Such a value allows for almost 646,456,974 digits, which is more than large enough + // for typical scenarios. If user code requires more than this, they should likely + // roll their own type that utilizes native memory and other specialized techniques. + internal static int MaxLength => Array.MaxLength / kcbitUint; + // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits internal readonly int _sign; // Do not rename (binary serialization) @@ -487,22 +496,22 @@ internal BigInteger(int n, uint[]? rgu) /// The bool indicating the sign of the value. private BigInteger(ReadOnlySpan value, bool negative) { + // Try to conserve space as much as possible by checking for wasted leading span entries + // sometimes the span has leading zeros from bit manipulation operations & and ^ + + int length = value.LastIndexOfAnyExcept(0u) + 1; + value = value[..length]; + if (value.Length > MaxLength) { ThrowHelper.ThrowOverflowException(); } - int len; - - // Try to conserve space as much as possible by checking for wasted leading span entries - // sometimes the span has leading zeros from bit manipulation operations & and ^ - for (len = value.Length; len > 0 && value[len - 1] == 0; len--); - - if (len == 0) + if (value.Length == 0) { - this = s_bnZeroInt; + this = default; } - else if (len == 1 && value[0] < kuMaskHighBit) + else if (value.Length == 1 && value[0] < kuMaskHighBit) { // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign _sign = negative ? -(int)value[0] : (int)value[0]; @@ -516,7 +525,7 @@ private BigInteger(ReadOnlySpan value, bool negative) else { _sign = negative ? -1 : +1; - _bits = value.Slice(0, len).ToArray(); + _bits = value.ToArray(); } AssertValid(); } @@ -527,87 +536,88 @@ private BigInteger(ReadOnlySpan value, bool negative) /// private BigInteger(Span value) { + bool isNegative; + int length; + + if ((value.Length > 0) && ((int)value[^1] < 0)) + { + isNegative = true; + length = value.LastIndexOfAnyExcept(uint.MaxValue) + 1; + + if ((length == 0) || ((int)value[length - 1] > 0)) + { + // We ne need to preserve the sign bit + length++; + } + Debug.Assert((int)value[length - 1] < 0); + } + else + { + isNegative = false; + length = value.LastIndexOfAnyExcept(0u) + 1; + } + value = value[..length]; + if (value.Length > MaxLength) { ThrowHelper.ThrowOverflowException(); } - int dwordCount = value.Length; - bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & kuMaskHighBit) == kuMaskHighBit); - - // Try to conserve space as much as possible by checking for wasted leading span entries - while (dwordCount > 0 && value[dwordCount - 1] == 0) dwordCount--; - - if (dwordCount == 0) + if (value.Length == 0) { - // BigInteger.Zero + // 0 this = s_bnZeroInt; - AssertValid(); - return; } - if (dwordCount == 1) + else if (value.Length == 1) { - if (unchecked((int)value[0]) < 0 && !isNegative) + if (isNegative) { - _bits = new uint[1]; - _bits[0] = value[0]; - _sign = +1; + if (value[0] == uint.MaxValue) + { + // -1 + this = s_bnMinusOneInt; + } + else if (value[0] == kuMaskHighBit) + { + // int.MinValue + this = s_bnMinInt; + } + else + { + _sign = unchecked((int)value[0]); + _bits = null; + } } - // Handle the special cases where the BigInteger likely fits into _sign - else if (int.MinValue == unchecked((int)value[0])) + else if (unchecked((int)value[0]) < 0) { - this = s_bnMinInt; + _sign = +1; + _bits = [value[0]]; } else { _sign = unchecked((int)value[0]); _bits = null; } - AssertValid(); - return; } - - if (!isNegative) + else { - // Handle the simple positive value cases where the input is already in sign magnitude - _sign = +1; - value = value.Slice(0, dwordCount); - _bits = value.ToArray(); - AssertValid(); - return; - } - - // Finally handle the more complex cases where we must transform the input into sign magnitude - NumericsHelpers.DangerousMakeTwosComplement(value); // mutates val + if (isNegative) + { + NumericsHelpers.DangerousMakeTwosComplement(value); - // Pack _bits to remove any wasted space after the twos complement - int len = value.Length; - while (len > 0 && value[len - 1] == 0) len--; + // Retrim any leading zeros carried from the sign + length = value.LastIndexOfAnyExcept(0u) + 1; + value = value[..length]; - // The number is represented by a single dword - if (len == 1 && unchecked((int)(value[0])) > 0) - { - if (value[0] == 1 /* abs(-1) */) - { - this = s_bnMinusOneInt; - } - else if (value[0] == kuMaskHighBit /* abs(Int32.MinValue) */) - { - this = s_bnMinInt; + _sign = -1; } else { - _sign = (-1) * ((int)value[0]); - _bits = null; + _sign = +1; } - } - else - { - _sign = -1; - _bits = value.Slice(0, len).ToArray(); + _bits = value.ToArray(); } AssertValid(); - return; } public static BigInteger Zero { get { return s_bnZeroInt; } } @@ -616,8 +626,6 @@ private BigInteger(Span value) public static BigInteger MinusOne { get { return s_bnMinusOneInt; } } - internal static int MaxLength => Array.MaxLength / sizeof(uint); - public bool IsPowerOfTwo { get diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs index da6ac2615b9c1..404902f61972e 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -29,19 +29,21 @@ public static void BigShiftsTest() public static void LargeNegativeBigIntegerShiftTest() { // Create a very large negative BigInteger - BigInteger bigInt = new BigInteger(-1) << int.MaxValue; - Assert.Equal(2147483647, bigInt.GetBitLength()); + int bitsPerElement = 8 * sizeof(uint); + int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement); + BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1); + Assert.Equal(maxBitLength - 1, bigInt.GetBitLength()); Assert.Equal(-1, bigInt.Sign); // Validate internal representation. - // At this point, bigInt should be a 1 followed by int.MaxValue zeros. + // At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros. // Given this, bigInt._bits is expected to be structured as follows: - // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) + // - _bits.Length == ceil(maxBitLength / bitsPerElement) // - First (_bits.Length - 1) elements: 0x00000000 // - Last element: 0x80000000 // ^------ (There's the leading '1') - Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)bigInt._bits.Length); + Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length); uint i = 0; for (; i < (bigInt._bits.Length - 1); i++) { @@ -52,18 +54,18 @@ public static void LargeNegativeBigIntegerShiftTest() // Right shift the BigInteger BigInteger shiftedBigInt = bigInt >> 1; - Assert.Equal(2147483646, shiftedBigInt.GetBitLength()); + Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength()); Assert.Equal(-1, shiftedBigInt.Sign); // Validate internal representation. - // At this point, shiftedBigInt should be a 1 followed by int.MaxValue - 1 zeros. + // At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros. // Given this, shiftedBigInt._bits is expected to be structured as follows: - // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) + // - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement) // - First (_bits.Length - 1) elements: 0x00000000 // - Last element: 0x40000000 // ^------ (the '1' is now one position to the right) - Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)shiftedBigInt._bits.Length); + Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length); i = 0; for (; i < (shiftedBigInt._bits.Length - 1); i++) { diff --git a/src/libraries/System.Runtime/README.md b/src/libraries/System.Runtime/README.md index 9bde5e23be48d..0d76cbe065cc6 100644 --- a/src/libraries/System.Runtime/README.md +++ b/src/libraries/System.Runtime/README.md @@ -1,7 +1,7 @@ # System.Runtime Contains fundamental classes and base classes that define commonly-used value and reference data types, events and event handlers, interfaces, attributes, and processing exceptions. -Documentation can be found here: https://learn.microsoft.com/en-us/dotnet/api/system?view=net-7.0. +Documentation can be found here: https://learn.microsoft.com/dotnet/api/system?view=net-7.0. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../../libraries/README.md#primary-bar) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 37bd783042bdb..33614371d6268 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -55,22 +55,174 @@ public AccessViolationException(string? message) { } public AccessViolationException(string? message, System.Exception? innerException) { } } public delegate void Action(); - public delegate void Action(T obj); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); - public delegate void Action(T1 arg1, T2 arg2); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); + public delegate void Action(T obj) + where T : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2) + where T1 : allows ref struct + where T2 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct; + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where T16 : allows ref struct; + public static partial class Activator { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Type and its constructor could be removed")] @@ -91,7 +243,7 @@ public static partial class Activator public static System.Runtime.Remoting.ObjectHandle? CreateInstanceFrom(string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Type and its constructor could be removed")] public static System.Runtime.Remoting.ObjectHandle? CreateInstanceFrom(string assemblyFile, string typeName, object?[]? activationAttributes) { throw null; } - public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { throw null; } + public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() where T : allows ref struct { throw null; } } public partial class AggregateException : System.Exception { @@ -1110,7 +1262,7 @@ public sealed partial class CLSCompliantAttribute : System.Attribute public CLSCompliantAttribute(bool isCompliant) { } public bool IsCompliant { get { throw null; } } } - public delegate int Comparison(T x, T y); + public delegate int Comparison(T x, T y) where T : allows ref struct; public abstract partial class ContextBoundObject : System.MarshalByRefObject { protected ContextBoundObject() { } @@ -1589,7 +1741,7 @@ public static partial class Convert public static bool TryToHexString(System.ReadOnlySpan source, System.Span destination, out int charsWritten) { throw null; } public static bool TryToHexStringLower(System.ReadOnlySpan source, System.Span destination, out int charsWritten) { throw null; } } - public delegate TOutput Converter(TInput input); + public delegate TOutput Converter(TInput input) where TInput : allows ref struct where TOutput : allows ref struct; public readonly partial struct DateOnly : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; @@ -2694,23 +2846,193 @@ protected FormattableString() { } public override string ToString() { throw null; } public abstract string ToString(System.IFormatProvider? formatProvider); } - public delegate TResult Func(); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); - public delegate TResult Func(T arg); - public delegate TResult Func(T1 arg1, T2 arg2); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + public delegate TResult Func() + where TResult : allows ref struct; + + public delegate TResult Func(T arg) + where T : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2) + where T1 : allows ref struct + where T2 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where TResult : allows ref struct; + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) + where T1 : allows ref struct + where T2 : allows ref struct + where T3 : allows ref struct + where T4 : allows ref struct + where T5 : allows ref struct + where T6 : allows ref struct + where T7 : allows ref struct + where T8 : allows ref struct + where T9 : allows ref struct + where T10 : allows ref struct + where T11 : allows ref struct + where T12 : allows ref struct + where T13 : allows ref struct + where T14 : allows ref struct + where T15 : allows ref struct + where T16 : allows ref struct + where TResult : allows ref struct; + public static partial class GC { public static int MaxGeneration { get { throw null; } } @@ -4635,7 +4957,7 @@ protected PlatformNotSupportedException(System.Runtime.Serialization.Serializati public PlatformNotSupportedException(string? message) { } public PlatformNotSupportedException(string? message, System.Exception? inner) { } } - public delegate bool Predicate(T obj); + public delegate bool Predicate(T obj) where T : allows ref struct; public partial class Progress : System.IProgress { public Progress() { } @@ -5286,7 +5608,7 @@ public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, in public void CopyTo(System.Span destination) { } public static string Create(System.IFormatProvider? provider, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute("provider")] ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { throw null; } public static string Create(System.IFormatProvider? provider, System.Span initialBuffer, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute(new string[]{ "provider", "initialBuffer"})] ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { throw null; } - public static string Create(int length, TState state, System.Buffers.SpanAction action) { throw null; } + public static string Create(int length, TState state, System.Buffers.SpanAction action) where TState : allows ref struct { throw null; } public bool EndsWith(char value) { throw null; } public bool EndsWith(string value) { throw null; } public bool EndsWith(string value, bool ignoreCase, System.Globalization.CultureInfo? culture) { throw null; } @@ -7498,11 +7820,11 @@ public enum OperationStatus NeedMoreData = 2, InvalidData = 3, } - public delegate void ReadOnlySpanAction(System.ReadOnlySpan span, TArg arg); + public delegate void ReadOnlySpanAction(System.ReadOnlySpan span, TArg arg) where TArg : allows ref struct; public static partial class SearchValues { - public static System.Buffers.SearchValues Create(System.ReadOnlySpan values) { throw null; } - public static System.Buffers.SearchValues Create(System.ReadOnlySpan values) { throw null; } + public static System.Buffers.SearchValues Create(params System.ReadOnlySpan values) { throw null; } + public static System.Buffers.SearchValues Create(params System.ReadOnlySpan values) { throw null; } public static System.Buffers.SearchValues Create(System.ReadOnlySpan values, System.StringComparison comparisonType) { throw null; } } public partial class SearchValues where T : System.IEquatable? @@ -7510,7 +7832,7 @@ public partial class SearchValues where T : System.IEquatable? internal SearchValues() { } public bool Contains(T value) { throw null; } } - public delegate void SpanAction(System.Span span, TArg arg); + public delegate void SpanAction(System.Span span, TArg arg) where TArg : allows ref struct; } namespace System.Buffers.Text { @@ -7811,11 +8133,17 @@ public partial interface IStructuralEquatable } namespace System.Collections.Generic { - public partial interface IAsyncEnumerable + public interface IAlternateEqualityComparer where TAlternate : allows ref struct + { + bool Equals(TAlternate alternate, T other); + int GetHashCode(TAlternate alternate); + T Create(TAlternate alternate); + } + public partial interface IAsyncEnumerable where T : allows ref struct { System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } - public partial interface IAsyncEnumerator : System.IAsyncDisposable + public partial interface IAsyncEnumerator : System.IAsyncDisposable where T : allows ref struct { T Current { get; } System.Threading.Tasks.ValueTask MoveNextAsync(); @@ -7844,11 +8172,11 @@ public partial interface IDictionary : System.Collections.Generic. bool Remove(TKey key); bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value); } - public partial interface IEnumerable : System.Collections.IEnumerable + public partial interface IEnumerable : System.Collections.IEnumerable where T : allows ref struct { new System.Collections.Generic.IEnumerator GetEnumerator(); } - public partial interface IEnumerator : System.Collections.IEnumerator, System.IDisposable + public partial interface IEnumerator : System.Collections.IEnumerator, System.IDisposable where T : allows ref struct { new T Current { get; } } @@ -13219,7 +13547,7 @@ public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServi public static T[] GetSubArray(T[] array, System.Range range) { throw null; } public static object GetUninitializedObject([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { } - public static bool IsReferenceOrContainsReferences() { throw null; } + public static bool IsReferenceOrContainsReferences() where T: allows ref struct { throw null; } [System.ObsoleteAttribute("The Constrained Execution Region (CER) feature is not supported.", DiagnosticId="SYSLIB0004", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public static void PrepareConstrainedRegions() { } [System.ObsoleteAttribute("The Constrained Execution Region (CER) feature is not supported.", DiagnosticId="SYSLIB0004", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] @@ -13340,26 +13668,26 @@ public TypeForwardedToAttribute(System.Type destination) { } } public static partial class Unsafe { - public static ref T AddByteOffset(ref T source, System.IntPtr byteOffset) { throw null; } + public static ref T AddByteOffset(ref T source, System.IntPtr byteOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public static ref T AddByteOffset(ref T source, nuint byteOffset) { throw null; } + public static ref T AddByteOffset(ref T source, nuint byteOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static void* Add(void* source, int elementOffset) { throw null; } - public static ref T Add(ref T source, int elementOffset) { throw null; } - public static ref T Add(ref T source, System.IntPtr elementOffset) { throw null; } + public unsafe static void* Add(void* source, int elementOffset) where T : allows ref struct { throw null; } + public static ref T Add(ref T source, int elementOffset) where T : allows ref struct { throw null; } + public static ref T Add(ref T source, System.IntPtr elementOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public static ref T Add(ref T source, nuint elementOffset) { throw null; } - public static bool AreSame([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) { throw null; } + public static ref T Add(ref T source, nuint elementOffset) where T : allows ref struct { throw null; } + public static bool AreSame([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static void* AsPointer(ref T value) { throw null; } + public unsafe static void* AsPointer(ref T value) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static ref T AsRef(void* source) { throw null; } - public static ref T AsRef(scoped ref readonly T source) { throw null; } + public unsafe static ref T AsRef(void* source) where T : allows ref struct { throw null; } + public static ref T AsRef(scoped ref readonly T source) where T : allows ref struct { throw null; } [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("o")] public static T? As(object? o) where T : class? { throw null; } - public static ref TTo As(ref TFrom source) { throw null; } - public static TTo BitCast(TFrom source) { throw null; } - public static System.IntPtr ByteOffset([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T origin, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T target) { throw null; } + public static ref TTo As(ref TFrom source) where TFrom : allows ref struct where TTo : allows ref struct { throw null; } + public static TTo BitCast(TFrom source) where TFrom : allows ref struct where TTo : allows ref struct { throw null; } + public static System.IntPtr ByteOffset([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T origin, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T target) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] public static void CopyBlock(ref byte destination, ref readonly byte source, uint byteCount) { } [System.CLSCompliantAttribute(false)] @@ -13369,9 +13697,9 @@ public static void CopyBlockUnaligned(ref byte destination, ref readonly byte so [System.CLSCompliantAttribute(false)] public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount) { } [System.CLSCompliantAttribute(false)] - public unsafe static void Copy(void* destination, ref readonly T source) { } + public unsafe static void Copy(void* destination, ref readonly T source) where T : allows ref struct { } [System.CLSCompliantAttribute(false)] - public unsafe static void Copy(ref T destination, void* source) { } + public unsafe static void Copy(ref T destination, void* source) where T : allows ref struct { } [System.CLSCompliantAttribute(false)] public static void InitBlock(ref byte startAddress, byte value, uint byteCount) { } [System.CLSCompliantAttribute(false)] @@ -13380,32 +13708,32 @@ public unsafe static void InitBlock(void* startAddress, byte value, uint byteCou public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) { } [System.CLSCompliantAttribute(false)] public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { } - public static bool IsAddressGreaterThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) { throw null; } - public static bool IsAddressLessThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) { throw null; } - public static bool IsNullRef(ref readonly T source) { throw null; } - public static ref T NullRef() { throw null; } - public static T ReadUnaligned(ref readonly byte source) { throw null; } + public static bool IsAddressGreaterThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; } + public static bool IsAddressLessThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; } + public static bool IsNullRef(ref readonly T source) where T : allows ref struct { throw null; } + public static ref T NullRef() where T : allows ref struct { throw null; } + public static T ReadUnaligned(scoped ref readonly byte source) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static T ReadUnaligned(void* source) { throw null; } + public unsafe static T ReadUnaligned(void* source) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static T Read(void* source) { throw null; } - public static int SizeOf() { throw null; } - public static void SkipInit(out T value) { throw null; } - public static ref T SubtractByteOffset(ref T source, System.IntPtr byteOffset) { throw null; } + public unsafe static T Read(void* source) where T : allows ref struct { throw null; } + public static int SizeOf() where T : allows ref struct { throw null; } + public static void SkipInit(out T value) where T : allows ref struct { throw null; } + public static ref T SubtractByteOffset(ref T source, System.IntPtr byteOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public static ref T SubtractByteOffset(ref T source, nuint byteOffset) { throw null; } + public static ref T SubtractByteOffset(ref T source, nuint byteOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public unsafe static void* Subtract(void* source, int elementOffset) { throw null; } - public static ref T Subtract(ref T source, int elementOffset) { throw null; } - public static ref T Subtract(ref T source, System.IntPtr elementOffset) { throw null; } + public unsafe static void* Subtract(void* source, int elementOffset) where T : allows ref struct { throw null; } + public static ref T Subtract(ref T source, int elementOffset) where T : allows ref struct { throw null; } + public static ref T Subtract(ref T source, System.IntPtr elementOffset) where T : allows ref struct { throw null; } [System.CLSCompliantAttribute(false)] - public static ref T Subtract(ref T source, nuint elementOffset) { throw null; } + public static ref T Subtract(ref T source, nuint elementOffset) where T : allows ref struct { throw null; } public static ref T Unbox(object box) where T : struct { throw null; } - public static void WriteUnaligned(ref byte destination, T value) { } + public static void WriteUnaligned(ref byte destination, T value) where T : allows ref struct { } [System.CLSCompliantAttribute(false)] - public unsafe static void WriteUnaligned(void* destination, T value) { } + public unsafe static void WriteUnaligned(void* destination, T value) where T : allows ref struct { } [System.CLSCompliantAttribute(false)] - public unsafe static void Write(void* destination, T value) { } + public unsafe static void Write(void* destination, T value) where T : allows ref struct { } } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false, Inherited=false)] public sealed partial class UnsafeAccessorAttribute : System.Attribute diff --git a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml index 51e030bd88c0c..d2ea84261c4b5 100644 --- a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0002 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 9ee808a6282e4..e05bcbf453e91 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 @@ -12,108 +12,52 @@ namespace System.Globalization.Tests { public class CompareInfoHashCodeTests : CompareInfoTestsBase { - 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 }; - yield return new object[] { s_invariantCompare, "igloo", "\u0130GLOO", CompareOptions.None }; - yield return new object[] { s_invariantCompare, "igloo", "IGLOO", CompareOptions.IgnoreCase }; - yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; - 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 }; - - if (!PlatformDetection.IsHybridGlobalizationOnBrowser) - { - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - 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 - } - } - [Theory] - [MemberData(nameof(HashCodeLocalized_TestData))] - public void HashCodeLocalized(CompareInfo cmpInfo, string str1, string str2, CompareOptions options) + [OuterLoop] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] + public void CheckHashingInLineWithEqual() { - 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 + int additionalCollisions = 0; + CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); + foreach (CultureInfo culture in cultures) { - Assert.False(areHashCodesEqual); - } - - // implication of the above behavior: - StringComparer stringComparer = new CustomComparer(cmpInfo, options); - TryAddToCustomDictionary(stringComparer, str1, str2, areHashCodesEqual); - } + // Japanese does not have "None" compare option, it always ignores Kana + // HashCode is not available for options different than None or IgnoreCase + if (culture.Name.Split('-')[0] == "ja") + continue; - 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); + for (int i = 0; i <= 65535; i++) + CheckChar(i, culture); } - catch (Exception ex) + + void CheckChar(int charCode, CultureInfo culture) { - Assert.Fail($"Unexpected exception thrown: {ex}"); + var cmpInfo = culture.CompareInfo; + char character = (char)charCode; + 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; + StringComparer stringComparer = new CustomComparer(cmpInfo, options); + // if equal => same, then expect hash => same + if (stringComparer.Compare(str1, str2) == 0) + { + Assert.True(areHashCodesEqual, $"Expected equal hashes for equal strings. The check failed for culture {culture.Name}, character: {character}, code: {charCode}."); + } + // if equal => diff, then expect hash => diff + else + { + if (areHashCodesEqual) + { + additionalCollisions++; // this should be smallest possible, 11541466 + } + } } } - 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 - } - - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] - [MemberData(nameof(CheckHashingOfSkippedChars_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] - 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); - } - public static IEnumerable GetHashCodeTestData => new[] { new object[] { "abc", CompareOptions.OrdinalIgnoreCase, "ABC", CompareOptions.OrdinalIgnoreCase, true }, 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 c921165a4f3c9..9a9523784716c 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 @@ -430,7 +430,7 @@ public void Ctor_String_Invalid() [InlineData(0x4C00)] public void TestCreationWithTemporaryLCID(int lcid) { - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/926e694f-1797-4418-a922-343d1c5e91a6 + // https://learn.microsoft.com/openspecs/windows_protocols/ms-lcid/926e694f-1797-4418-a922-343d1c5e91a6 // If a temporary LCID is assigned it will be dynamically assigned at runtime to be // 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, 0x4000, 0x4400, 0x4800, or 0x4C00, // for the valid language-script-region tags. diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes_SafeFileHandle.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes_SafeFileHandle.cs index 57eb9f80337b6..1cf0bb62e3bd5 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes_SafeFileHandle.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes_SafeFileHandle.cs @@ -101,7 +101,7 @@ public async Task WritingShouldUpdateWriteTime_After_SetLastAccessTime() File.SetLastAccessTime(handle, DateTime.Now.Subtract(TimeSpan.FromDays(1))); DateTime timeAfterWrite = default, timeBeforeWrite = File.GetLastWriteTime(handle); - // According to https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-win32_file_attribute_data + // According to https://learn.microsoft.com/windows/win32/api/fileapi/ns-fileapi-win32_file_attribute_data // write time has a resolution of 2 seconds on FAT. Let's wait a little bit longer. for (int i = 0; i <= 5 && timeBeforeWrite >= timeAfterWrite; i++) { diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/ManualTests/ManualTests.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/ManualTests/ManualTests.cs index fe0395a80c284..b4c7b3d7d216c 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/ManualTests/ManualTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/ManualTests/ManualTests.cs @@ -112,7 +112,7 @@ public static void SetLength_DoesNotAlterPositionWhenNativeCallFails() - Create an 8mb fixed size VHD. - Open Computer Management -> Storage -> Disk Management - Follow these instructions: - https://docs.microsoft.com/en-us/windows-server/storage/disk-management/manage-virtual-hard-disks + https://learn.microsoft.com/windows-server/storage/disk-management/manage-virtual-hard-disks - Restrict the space available in the VHD. - Create a 512 bytes quota in the VHD created above using cmd: diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOInputs.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOInputs.cs index 0faa81495ddf4..9676973fed783 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOInputs.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOInputs.cs @@ -220,7 +220,7 @@ private static string GetLongPath(string rootPath, int characterCount, bool exte } public static IEnumerable GetReservedDeviceNames() - { // See: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + { // See: https://learn.microsoft.com/windows/win32/fileio/naming-a-file // // Note - Recent versions of Win10 relax this restriction and allow reserved // device names as filenames. diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/TestData.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/TestData.cs index 7b159b016b173..0451d386cbfcd 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/TestData.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/TestData.cs @@ -7,7 +7,7 @@ internal static class TestData { - // see: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file + // see: https://learn.microsoft.com/windows/desktop/FileIO/naming-a-file private static readonly char[] s_invalidFileNameChars = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new char[] { diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs index 84a1fc2b1c447..bc847ff3f21e6 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs @@ -110,7 +110,7 @@ public void BinaryWriter_WriteCharTest2() Stream stream = CreateStream(); // string name = iso-2022-jp, codepage = 50220 (original test used a code page number). - // taken from https://docs.microsoft.com/en-us/windows/desktop/Intl/code-page-identifiers + // taken from https://learn.microsoft.com/windows/desktop/Intl/code-page-identifiers string codepageName = "iso-2022-jp"; BinaryWriter writer = new BinaryWriter(stream, Encoding.GetEncoding(codepageName)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs index 70d55cd6f6985..3df92cd860935 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.CompilerServices.Unsafe.Tests/UnsafeTests.cs @@ -6,6 +6,8 @@ using System.Runtime.InteropServices; using Xunit; +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + namespace System.Runtime.CompilerServices { public class UnsafeTests @@ -19,6 +21,17 @@ public static unsafe void ReadInt32() Assert.Equal(expected, ret); } + [Fact] + public static unsafe void ReadSpan() + { + int i = 42; + Span span = new Span(ref i); + void* address = Unsafe.AsPointer(ref span); // Unsafe.AsPointer is safe since expected is on stack + Span ret = Unsafe.Read>(address); + Assert.Equal(1, ret.Length); + Assert.Equal(42, ret[0]); + } + [Fact] public static unsafe void WriteInt32() { @@ -32,6 +45,20 @@ public static unsafe void WriteInt32() Assert.Equal(expected, Unsafe.Read(address)); } + [Fact] + public static unsafe void WriteSpan() + { + int value = 10; + int value2 = 20; + Span span = new Span(ref value); + Span* address = (Span*)Unsafe.AsPointer(ref span); // Unsafe.AsPointer is safe since value is on stack + Unsafe.Write(address, new Span(ref value2)); + + Assert.Equal(1, span.Length); + Assert.Equal(20, span[0]); + Assert.True(Unsafe.AreSame(ref value2, ref span[0])); + } + [Fact] public static unsafe void WriteBytesIntoInt32() { @@ -197,6 +224,7 @@ public static unsafe void SizeOf() Assert.Equal(4, Unsafe.SizeOf()); Assert.Equal(8, Unsafe.SizeOf()); Assert.Equal(512, Unsafe.SizeOf()); + Assert.Equal(IntPtr.Size * 2, Unsafe.SizeOf>()); } [Theory] @@ -496,6 +524,19 @@ public static unsafe void AsRef() } } + [Fact] + public static unsafe void AsRef_RefStruct() + { + int i = 42; + Span span = new Span(ref i); + + Span* spanPtr = &span; + fixed (Span* spanPtrActual = &Unsafe.AsRef>(in span)) + { + Assert.Equal((IntPtr)spanPtr, (IntPtr)spanPtrActual); + } + } + [Fact] public static void InAsRef() { @@ -577,6 +618,33 @@ public static void RefAddIntPtr() Assert.Equal(0x123, r3); } + [Fact] + public static void RefAddIntPtr_RefStruct() + { + int i0 = 42, i1 = 43, i2 = 44, i3 = 45; + FourSpans span = new() + { + Span0 = new Span(ref i0), + Span1 = new Span(ref i1), + Span2 = new Span(ref i2), + Span3 = new Span(ref i3) + }; + + ref Span spanRef = ref Unsafe.AsRef(in span.Span0); + Assert.Equal(42, spanRef[0]); + + spanRef = ref Unsafe.Add(ref spanRef, 1); + Assert.Equal(43, spanRef[0]); + + spanRef = ref Unsafe.Add(ref spanRef, 2); + Assert.Equal(45, spanRef[0]); + } + + private ref struct FourSpans + { + public Span Span0, Span1, Span2, Span3; + } + [Fact] public static void RefAddNuint() { @@ -1123,6 +1191,11 @@ public static unsafe void IsNullRef_Null() Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + // Validate that calling on ref structs works. + + Assert.True(Unsafe.IsNullRef>(ref Unsafe.NullRef>())); + Assert.True(Unsafe.IsNullRef>(ref Unsafe.NullRef>())); + // Validate on ref created from a pointer int* p = (int*)0; @@ -1188,6 +1261,13 @@ public static unsafe void BitCast() Assert.Equal(int.MinValue, Unsafe.BitCast(0x8000_0000u)); Assert.Equal(0x8000_0000u, Unsafe.BitCast(int.MinValue)); + // Conversion between same sized ref structs should succeed + + // TODO https://github.com/dotnet/runtime/issues/102988: Uncomment once this works on mono + //int i = 42; + //Span span = Unsafe.BitCast, Span>(new ReadOnlySpan(&i, 1)); + //Assert.Equal(42, span[0]); + // Conversion from runtime SIMD type to a custom struct should succeed Vector4 vector4a = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index 1e6b01ecf75b3..1c9fa97e35c6d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -183,6 +183,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/Generic/IEnumerableTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/Generic/IEnumerableTests.cs new file mode 100644 index 0000000000000..110679add7559 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/Generic/IEnumerableTests.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using Xunit; + +namespace System.Collections.Generic.Tests +{ + public class IEnumerableTests + { + [Fact] + public void SupportsRefStructT() + { + StringBuilder builder = new(); + foreach (ReadOnlySpan c in new MySpanReturningEnumerable("hello")) + { + builder.Append(c); + } + Assert.Equal("hello", builder.ToString()); + } + } + + internal sealed class MySpanReturningEnumerable(string value) : IEnumerable>, IEnumerator> + { + private int _index = -1; + + public IEnumerator> GetEnumerator() => this; + IEnumerator IEnumerable.GetEnumerator() => this; + + public ReadOnlySpan Current => value.AsSpan(_index, 1); + + public bool MoveNext() => ++_index < value.Length; + + public void Dispose() { } + + object IEnumerator.Current => throw new NotSupportedException(); + + void IEnumerator.Reset() => throw new NotSupportedException(); + } +} diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 9e2dc8059fa8b..2fd5267409cb6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -365,6 +365,12 @@ public static void IsReferenceOrContainsReferences() Assert.False(RuntimeHelpers.IsReferenceOrContainsReferences()); Assert.False(RuntimeHelpers.IsReferenceOrContainsReferences()); Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences()); + Assert.False(RuntimeHelpers.IsReferenceOrContainsReferences()); + Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences()); + Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences>()); + Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences>()); + Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences()); + Assert.True(RuntimeHelpers.IsReferenceOrContainsReferences()); } [Fact] @@ -409,6 +415,7 @@ public static unsafe void AllocateTypeAssociatedMemoryValidArguments() Assert.True(new Span((void*)memory, 32).SequenceEqual(new byte[32])); } +#pragma warning disable CS0649 [StructLayoutAttribute(LayoutKind.Sequential)] private struct StructWithoutReferences { @@ -422,6 +429,29 @@ private struct StructWithReferences public object d; } + private ref struct RefStructWithoutReferences + { + public int a; + public long b; + } + + private ref struct RefStructWithReferences + { + public int a; + public object b; + } + + private ref struct RefStructWithRef + { + public ref int a; + } + + private ref struct RefStructWithNestedRef + { + public Span a; + } +#pragma warning restore CS0649 + [Fact] public static void FixedAddressValueTypeTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/JitInfoTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/JitInfoTests.cs index 18e05e2380e80..2b2472f1b22e8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/JitInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/JitInfoTests.cs @@ -13,7 +13,7 @@ public class JitInfoTests { private long MakeAndInvokeDynamicSquareMethod(int input) { - // example ref emit dynamic method from https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-define-and-execute-dynamic-methods + // example ref emit dynamic method from https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/how-to-define-and-execute-dynamic-methods Type[] methodArgs = {typeof(int)}; DynamicMethod squareIt = new DynamicMethod( 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 35514e9c381fa..543f790f29ee3 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 @@ -231,6 +231,80 @@ static void RunTest(IEqualityComparer comparer, CompareInfo expectedComp } } + [Fact] + public void Comparers_AllImplementAlternateComparer() + { + Assert.IsAssignableFrom, string>>(StringComparer.Ordinal); + Assert.IsAssignableFrom, string>>(StringComparer.OrdinalIgnoreCase); + + Assert.IsAssignableFrom, string>>(StringComparer.CurrentCulture); + Assert.IsAssignableFrom, string>>(StringComparer.CurrentCultureIgnoreCase); + + Assert.IsAssignableFrom, string>>(StringComparer.InvariantCulture); + Assert.IsAssignableFrom, string>>(StringComparer.InvariantCultureIgnoreCase); + + Assert.IsAssignableFrom, string>>(StringComparer.Create(new CultureInfo("en-US"), false)); + Assert.IsAssignableFrom, string>>(StringComparer.Create(new CultureInfo("en-US"), true)); + + Assert.IsAssignableFrom, string>>(StringComparer.Create(new CultureInfo("en-US"), CompareOptions.IgnoreSymbols)); + } + + [Fact] + public void IAlternateEqualityComparer_SpansBehaveLikeStrings() + { + List caseSensitive = + [ + StringComparer.Ordinal, + StringComparer.CurrentCulture, + StringComparer.InvariantCulture, + StringComparer.Create(new CultureInfo("en-US"), false), + ]; + if (!PlatformDetection.IsHybridGlobalization) + { + // "CompareOptions = IgnoreSymbols are not supported when HybridGlobalization=true" + caseSensitive.Add(StringComparer.Create(new CultureInfo("en-US"), CompareOptions.IgnoreSymbols)); + } + + StringComparer[] caseInsensitive = + [ + StringComparer.OrdinalIgnoreCase, + StringComparer.CurrentCultureIgnoreCase, + StringComparer.InvariantCultureIgnoreCase, + StringComparer.Create(new CultureInfo("en-US"), true) + ]; + + StringComparer[] comparers = [.. caseSensitive, .. caseInsensitive]; + + foreach (StringComparer comparer in comparers) + { + IAlternateEqualityComparer, string> alternateComparer = (IAlternateEqualityComparer, string>)comparer; + + Assert.True(alternateComparer.Equals(ReadOnlySpan.Empty, "")); + Assert.False(alternateComparer.Equals(ReadOnlySpan.Empty, null)); + Assert.False(alternateComparer.Equals(null, null)); + + Assert.True(alternateComparer.Equals("hello".AsSpan(), "hello")); + Assert.False(alternateComparer.Equals("hello".AsSpan(), "yello")); + + Assert.Equal(comparer.GetHashCode("hello"), alternateComparer.GetHashCode("hello".AsSpan())); + + Assert.Same(string.Empty, alternateComparer.Create(ReadOnlySpan.Empty)); + Assert.Same(string.Empty, alternateComparer.Create(string.Empty)); + } + + foreach (IAlternateEqualityComparer, string> alternateComparer in caseSensitive) + { + Assert.False(alternateComparer.Equals("hello".AsSpan(), "HELLO")); + Assert.False(alternateComparer.Equals("HELLO".AsSpan(), "hello")); + } + + foreach (IAlternateEqualityComparer, string> alternateComparer in caseInsensitive) + { + Assert.True(alternateComparer.Equals("hello".AsSpan(), "HELLO")); + Assert.True(alternateComparer.Equals("HELLO".AsSpan(), "hello")); + } + } + private static IEqualityComparer GetNonRandomizedComparer(string name) { Type nonRandomizedComparerType = typeof(StringComparer).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer"); 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 9f59853b136da..8f8643be5b550 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 @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -148,6 +149,30 @@ public static void Create_ReturnsExpectedString(string expected) Assert.Equal(expected, result); } + [Fact] + public static void Create_CanSquirrelAwayArg() + { + object arg = new object(); + object storedArg = null; + string result = string.Create(5, arg, (span, arg) => + { + "hello".AsSpan().CopyTo(span); + storedArg = arg; + }); + Assert.Equal("hello", result); + Assert.Same(arg, storedArg); + } + + [Fact] + public static void Create_CanPassSpanAsArg() + { + string result = string.Create(5, "hello".AsSpan(), (span, arg) => + { + arg.CopyTo(span); + }); + Assert.Equal("hello", result); + } + [Fact] public static void Create_InterpolatedString_ConstructsStringAndClearsBuilder() { @@ -966,6 +991,29 @@ public static void GetHashCode_StringComparison(StringComparison comparisonType) Assert.Equal(hashCodeFromStringComparer, hashCodeFromStringGetHashCodeOfSpan); } + public static IEnumerable NonRandomizedGetHashCode_EquivalentForStringAndSpan_MemberData() => + from charValueLimit in new[] { 128, 256, char.MaxValue } + from ignoreCase in new[] { false, true } + select new object[] { charValueLimit, ignoreCase }; + + [Theory] + [MemberData(nameof(NonRandomizedGetHashCode_EquivalentForStringAndSpan_MemberData))] + public static void NonRandomizedGetHashCode_EquivalentForStringAndSpan(int charValueLimit, bool ignoreCase) + { + // This is testing internal API. If that API changes, this test will need to be updated. + const BindingFlags Flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic; + string suffix = ignoreCase ? "OrdinalIgnoreCase" : ""; + Func getStringHC = typeof(string).GetMethod($"GetNonRandomizedHashCode{suffix}", Flags, Type.EmptyTypes)!.CreateDelegate>(); + Func, int> getSpanHC = typeof(string).GetMethod($"GetNonRandomizedHashCode{suffix}", Flags, [typeof(ReadOnlySpan)])!.CreateDelegate, int>>(); + + var r = new Random(42); + for (int i = 0; i < 512; i++) + { + string s = new string(r.GetItems(Enumerable.Range(0, charValueLimit).Select(i => (char)i).ToArray(), i)); + Assert.Equal(getStringHC(s), getSpanHC(s.AsSpan())); + } + } + public static IEnumerable GetHashCode_NoSuchStringComparison_ThrowsArgumentException_Data => new[] { new object[] { StringComparisons.Min() - 1 }, diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml index 215e9b2af0541..edcc855106e56 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0002 diff --git a/src/libraries/System.Security.Cryptography.Xml/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Cryptography.Xml/src/CompatibilitySuppressions.xml index 4f60bbff6d578..3d73e1ec2e2dd 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Cryptography.Xml/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index ae5affcbb971a..4a7bd04e14eec 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -1210,6 +1210,8 @@ Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs" /> + choices, int length) ArgumentOutOfRangeException.ThrowIfNegative(length); -#pragma warning disable 8500 return string.Create(length, - (IntPtr)(&choices), - static (destination, state) => - { - GetItemsCore(*(ReadOnlySpan*)state, destination); - }); -#pragma warning restore 8500 + choices, + static (destination, choices) => GetItemsCore(choices, destination)); } /// diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedDirectoryStoreProvider.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedDirectoryStoreProvider.cs index da0fc5dff1580..decc699c4495d 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedDirectoryStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedDirectoryStoreProvider.cs @@ -46,6 +46,8 @@ internal SafeX509StackHandle GetNativeCollection() { _storeDirectoryInfo.Refresh(); DirectoryInfo info = _storeDirectoryInfo; + ret = _nativeCollection; + elapsed = _recheckStopwatch.Elapsed; if (ret == null || _forceRefresh || 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 1aa22406e4fd4..9c3fa0252ac69 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 @@ -93,6 +93,9 @@ private static Tuple GetCollections() { lock (s_recheckStopwatch) { + ret = s_nativeCollections; + elapsed = s_recheckStopwatch.Elapsed; + if (ret == null || elapsed > s_assumeInvalidInterval || LastWriteTimesHaveChanged()) @@ -214,7 +217,7 @@ bool ProcessFile(string file, out DateTime lastModified) if (processedFiles.Contains(fileId)) { - return true; + return true; } using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file, "rb")) diff --git a/src/libraries/System.Security.Cryptography/tests/CngUtility.cs b/src/libraries/System.Security.Cryptography/tests/CngUtility.cs index 9d23acdf063fa..b25ed045200f8 100644 --- a/src/libraries/System.Security.Cryptography/tests/CngUtility.cs +++ b/src/libraries/System.Security.Cryptography/tests/CngUtility.cs @@ -21,14 +21,14 @@ public static bool IsAlgorithmSupported(string algId, string implementation = MS return isSupported; } - // https://docs.microsoft.com/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider + // https://learn.microsoft.com/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider [DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern int BCryptCloseAlgorithmProvider( [In] IntPtr hAlgorithm, [In] uint dwFlags); - // https://docs.microsoft.com/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider + // https://learn.microsoft.com/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider [DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern int BCryptOpenAlgorithmProvider( diff --git a/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml index 3a242096603ac..ea58cd3565d11 100644 --- a/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0002 diff --git a/src/libraries/System.ServiceModel.Syndication/src/CompatibilitySuppressions.xml b/src/libraries/System.ServiceModel.Syndication/src/CompatibilitySuppressions.xml index 552f62c3f1f61..c915f43527f4e 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.ServiceModel.Syndication/src/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/System.ServiceProcess.ServiceController/README.md b/src/libraries/System.ServiceProcess.ServiceController/README.md index 1ccd12752449c..1d05ab223d493 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/README.md +++ b/src/libraries/System.ServiceProcess.ServiceController/README.md @@ -1,9 +1,9 @@ # System.ServiceProcess.ServiceController -Provides the [`System.ServiceProcess.ServiceController`](https://learn.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontroller) class, which allows you to connect to a Windows service, manipulate it, or get information about it. +Provides the [`System.ServiceProcess.ServiceController`](https://learn.microsoft.com/dotnet/api/system.serviceprocess.servicecontroller) class, which allows you to connect to a Windows service, manipulate it, or get information about it. Note that `System.ServiceProcess.ServiceController` supports Windows only. -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.serviceprocess. +Documentation can be found at https://learn.microsoft.com/dotnet/api/system.serviceprocess. ## Contribution Bar - [x] [We consider new features, new APIs and performance changes](../README.md#primary-bar) diff --git a/src/libraries/System.Text.Json/docs/KeyConverter_spec.md b/src/libraries/System.Text.Json/docs/KeyConverter_spec.md index b382e656e58a0..175834af08f7c 100644 --- a/src/libraries/System.Text.Json/docs/KeyConverter_spec.md +++ b/src/libraries/System.Text.Json/docs/KeyConverter_spec.md @@ -37,7 +37,7 @@ Implement an internal custom mechanism that is in charge of converting a defined * We need to define a criteria to choose what types we should support, I suggest to do as Utf8JsonReader/Writer and support the types supported by the Utf8Parser/Formatter. -* Supported types (Types supported by [`Utf8Formatter/Parser`](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.text.utf8formatter?view=netcore-3.1) + a few others that are popular): +* Supported types (Types supported by [`Utf8Formatter/Parser`](https://learn.microsoft.com/dotnet/api/system.buffers.text.utf8formatter?view=netcore-3.1) + a few others that are popular): * Boolean * Byte * DateTime diff --git a/src/libraries/System.Text.Json/docs/ThreatModel.md b/src/libraries/System.Text.Json/docs/ThreatModel.md index f0031f8340c07..97b18b9df17f9 100644 --- a/src/libraries/System.Text.Json/docs/ThreatModel.md +++ b/src/libraries/System.Text.Json/docs/ThreatModel.md @@ -10,17 +10,17 @@ The emphasis of this document is to describe security threats that were consider The notes in this section apply to the following serialization methods: -- [`JsonSerializer.Serialize`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serialize?view=net-5.0) -- [`JsonSerializer.SerializeAsync`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializeasync?view=net-5.0) -- [`JsonSerializer.SerializeToUtf8Bytes`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializetoutf8bytes?view=net-5.0) +- [`JsonSerializer.Serialize`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializer.serialize?view=net-5.0) +- [`JsonSerializer.SerializeAsync`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializeasync?view=net-5.0) +- [`JsonSerializer.SerializeToUtf8Bytes`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializetoutf8bytes?view=net-5.0) ### Threat: Stack overflow due to circular references -.NET object graphs existing at runtime are generally trusted as inputs to `Utf8JsonWriter` and `JsonSerializer.Serialize` when writing. However, circular references in the instantiated input object graphs may cause a [`StackOverflowException`](https://docs.microsoft.com/dotnet/api/system.stackoverflowexception?view=net-5.0) if not guarded against, and possibly cause the application process to crash. +.NET object graphs existing at runtime are generally trusted as inputs to `Utf8JsonWriter` and `JsonSerializer.Serialize` when writing. However, circular references in the instantiated input object graphs may cause a [`StackOverflowException`](https://learn.microsoft.com/dotnet/api/system.stackoverflowexception?view=net-5.0) if not guarded against, and possibly cause the application process to crash. #### Mitigation -Circular references are detected when writing due to a maximum depth setting (64 by default), and a clear [`JsonException`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonexception?view=net-5.0) is thrown when this depth is exceeded. Note that the reference handling feature implemented by the serializer provides a way to preserve object references on serialization and deserialization. +Circular references are detected when writing due to a maximum depth setting (64 by default), and a clear [`JsonException`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonexception?view=net-5.0) is thrown when this depth is exceeded. Note that the reference handling feature implemented by the serializer provides a way to preserve object references on serialization and deserialization. ### Threat: Unintentional information disclosure @@ -36,8 +36,8 @@ Circular references are detected when writing due to a maximum depth setting (64 The notes in this section apply to the following deserialization methods: -- [`JsonSerializer.Deserialize`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserialize?view=net-5.0) -- [`JsonSerializer.DeserializeAsync`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserializeasync?view=net-5.0) +- [`JsonSerializer.Deserialize`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserialize?view=net-5.0) +- [`JsonSerializer.DeserializeAsync`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserializeasync?view=net-5.0) The `Utf8JsonReader` class, leveraged by `JsonSerializer` for deserialization, accepts arbitrary UTF-8 text or binary data as input, and is therefore expected to handle data from potentially untrusted sources. @@ -79,7 +79,7 @@ This situation can be avoided by being aware of the following points: - Constructors run when `JsonSerializer.Deserialize` deserializes POCOs. Therefore, logic in constructors may participate in state management of the deserialized instance. -- If necessitated by your deserialization scenario, use callbacks to ensure that the object is in a valid state. This is not yet a first-class feature in the serializer, but a workaround is described in the [How to migrate from Newtonsoft.Json to System.Text.Json document](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#callbacks). Be aware that this workaround potentially degrades performance. +- If necessitated by your deserialization scenario, use callbacks to ensure that the object is in a valid state. This is not yet a first-class feature in the serializer, but a workaround is described in the [How to migrate from Newtonsoft.Json to System.Text.Json document](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#callbacks). Be aware that this workaround potentially degrades performance. - Where applicable, consider validating object graphs deserialized from untrusted data sources before processing them. Each individual object may be in a consistent state, but an object graph as a whole may not be. @@ -139,11 +139,11 @@ By default, the `JsonSerializer` and `Utf8JsonReader` strictly honor the [RFC 82 - When advancing through JSON payloads, the `Utf8JsonReader` validates that the next token does not violate RFC 8529. However, a deserialization converter may decide to validate that the next token is compatible with the target type they wish to obtain. For instance, if you are expecting a `JsonTokenType.Number` the reader will not validate that the next token is not a `JsonTokenType.StartObject`. For such cases, the converter could check the payload and throw a `JsonException` if it is invalid, similar to what the serializer would do. Otherwise, the reader will throw an `InvalidOperationException`. - If a `JsonException` or `NotSupportedException` is thrown from a custom converter without a message, the serializer will append a message that includes the path to the part of the JSON that caused the error. For more information on error handling in converters, see [How to write custom converters for JSON serialization (marshalling) in .NET](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#error-handling). + If a `JsonException` or `NotSupportedException` is thrown from a custom converter without a message, the serializer will append a message that includes the path to the part of the JSON that caused the error. For more information on error handling in converters, see [How to write custom converters for JSON serialization (marshalling) in .NET](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#error-handling). ## `JsonSerializerOptions` -The notes in this section apply to usages of the [`JsonSerializerOptions`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializeroptions?view=net-5.0) type. +The notes in this section apply to usages of the [`JsonSerializerOptions`](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializeroptions?view=net-5.0) type. ### Threat: Poor CPU throughput due to not caching custom `JsonSerializerOptions` instances @@ -193,7 +193,7 @@ Be sure to maintain an upper bound to the number of types being passed to `JsonS - The serializer, reader, and writer strictly honor the [RFC 8259 specification](https://tools.ietf.org/html/rfc8259) by default, but more permissive settings such as allowing trailing commas, allowing quoted numbers, and allowing, skipping, or disallowing both single-line and multi-line comments can be opted-into with `JsonSerializerOptions` and `JsonReaderOptions`. -- The serializer has a built-in converter for `DateTime` and `DateTimeOffset` types which parses and formats according to the ISO 8601:1-2019 extended profile. The specification and information about workarounds using custom converters is documented in the docs website: https://docs.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support. +- The serializer has a built-in converter for `DateTime` and `DateTimeOffset` types which parses and formats according to the ISO 8601:1-2019 extended profile. The specification and information about workarounds using custom converters is documented in the docs website: https://learn.microsoft.com/dotnet/standard/datetime/system-text-json-support. - The reference handling feature utilizes a non-standard JSON schema adopted from `Newtonsoft.Json` in which "$id", "$ref", and "$values" metadata properties are interspersed in JSON to allow keeping track of object references. When deserializing, this allows objects to point to each other in the resulting instance. When serializing, the resulting JSON is formatted using the previously mentioned representation. This feature is opt-in only (not enabled by default). When the feature is enabled, any deviation from the expected metadata in the input payloads when deserializing will cause a `JsonException` to be thrown. diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index febf787afeef8..62e9fd57d8299 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -20,6 +20,7 @@ private sealed partial class Emitter private const string CreateValueInfoMethodName = "CreateValueInfo"; private const string CtorParamInitMethodNameSuffix = "CtorParamInit"; private const string DefaultOptionsStaticVarName = "s_defaultOptions"; + private const string InstanceMemberBindingFlagsVariableName = "InstanceMemberBindingFlags"; private const string OriginatingResolverPropertyName = "OriginatingResolver"; private const string InfoVarName = "info"; private const string NumberHandlingPropName = "NumberHandling"; @@ -500,6 +501,7 @@ private SourceText GenerateForObject(ContextGenerationSpec contextSpec, TypeGene string? propInitMethodName = null; string? propInitAdapterFunc = null; + string? constructorInfoFactoryFunc = null; string? ctorParamMetadataInitMethodName = null; string? serializeMethodName = null; @@ -508,10 +510,20 @@ private SourceText GenerateForObject(ContextGenerationSpec contextSpec, TypeGene propInitMethodName = $"{typeFriendlyName}{PropInitMethodNameSuffix}"; propInitAdapterFunc = $"_ => {propInitMethodName}({OptionsLocalVariableName})"; - if (constructionStrategy == ObjectConstructionStrategy.ParameterizedConstructor) + if (constructionStrategy is ObjectConstructionStrategy.ParameterizedConstructor) { ctorParamMetadataInitMethodName = $"{typeFriendlyName}{CtorParamInitMethodNameSuffix}"; } + + if (constructionStrategy is ObjectConstructionStrategy.ParameterlessConstructor + or ObjectConstructionStrategy.ParameterizedConstructor) + { + string argTypes = typeMetadata.CtorParamGenSpecs.Count == 0 + ? "global::System.Array.Empty()" + : $$"""new[] {{{string.Join(", ", typeMetadata.CtorParamGenSpecs.Select(p => $"typeof({p.ParameterType.FullyQualifiedName})"))}}}"""; + + constructorInfoFactoryFunc = $"static () => typeof({typeMetadata.TypeRef.FullyQualifiedName}).GetConstructor({InstanceMemberBindingFlagsVariableName}, binder: null, {argTypes}, modifiers: null)"; + } } if (ShouldGenerateSerializationLogic(typeMetadata)) @@ -531,7 +543,8 @@ private SourceText GenerateForObject(ContextGenerationSpec contextSpec, TypeGene ObjectWithParameterizedConstructorCreator = {{parameterizedCreatorInvocation}}, PropertyMetadataInitializer = {{propInitAdapterFunc ?? "null"}}, ConstructorParameterMetadataInitializer = {{ctorParamMetadataInitMethodName ?? "null"}}, - {{SerializeHandlerPropName}} = {{serializeMethodName ?? "null"}} + ConstructorAttributeProviderFactory = {{constructorInfoFactoryFunc ?? "null"}}, + {{SerializeHandlerPropName}} = {{serializeMethodName ?? "null"}}, }; {{JsonTypeInfoLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.CreateObjectInfo<{{typeMetadata.TypeRef.FullyQualifiedName}}>({{OptionsLocalVariableName}}, {{ObjectInfoVarName}}); @@ -651,7 +664,8 @@ private void GeneratePropMetadataInitFunc(SourceWriter writer, string propInitMe IsExtensionData = {{FormatBoolLiteral(property.IsExtensionData)}}, NumberHandling = {{FormatNumberHandling(property.NumberHandling)}}, PropertyName = {{FormatStringLiteral(property.MemberName)}}, - JsonPropertyName = {{FormatStringLiteral(property.JsonPropertyName)}} + JsonPropertyName = {{FormatStringLiteral(property.JsonPropertyName)}}, + AttributeProviderFactory = static () => typeof({{property.DeclaringType.FullyQualifiedName}}).GetMember({{FormatStringLiteral(property.MemberName)}}, {{InstanceMemberBindingFlagsVariableName}}) is { Length: > 0 } results ? results[0] : null, }; properties[{{i}}] = {{JsonMetadataServicesTypeRef}}.CreatePropertyInfo<{{propertyTypeFQN}}>({{OptionsLocalVariableName}}, {{InfoVarName}}{{i}}); @@ -711,7 +725,7 @@ private static void GenerateCtorParamMetadataInitFunc(SourceWriter writer, strin ParameterType = typeof({{spec.ParameterType.FullyQualifiedName}}), Position = {{spec.ParameterIndex}}, HasDefaultValue = {{FormatBoolLiteral(spec.HasDefaultValue)}}, - DefaultValue = {{CSharpSyntaxUtilities.FormatLiteral(spec.DefaultValue, spec.ParameterType)}}, + DefaultValue = {{(spec.HasDefaultValue ? CSharpSyntaxUtilities.FormatLiteral(spec.DefaultValue, spec.ParameterType) : "null")}}, IsNullable = {{FormatBoolLiteral(spec.IsNullable)}}, }, """); @@ -735,6 +749,7 @@ private static void GenerateCtorParamMetadataInitFunc(SourceWriter writer, strin Name = {{FormatStringLiteral(spec.Name)}}, ParameterType = typeof({{spec.ParameterType.FullyQualifiedName}}), Position = {{spec.ParameterIndex}}, + IsMemberInitializer = true, }, """); @@ -1099,7 +1114,14 @@ private static SourceText GetRootJsonContextImplementation(ContextGenerationSpec GetLogicForDefaultSerializerOptionsInit(contextSpec.GeneratedOptionsSpec, writer); - writer.WriteLine(); + writer.WriteLine($""" + + private const global::System.Reflection.BindingFlags {InstanceMemberBindingFlagsVariableName} = + global::System.Reflection.BindingFlags.Instance | + global::System.Reflection.BindingFlags.Public | + global::System.Reflection.BindingFlags.NonPublic; + + """); writer.WriteLine($$""" /// diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index f50564ba081fc..6ce961115720b 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -1263,6 +1263,7 @@ public static partial class JsonMetadataServices public sealed partial class JsonObjectInfoValues { public JsonObjectInfoValues() { } + public System.Func? ConstructorAttributeProviderFactory { get { throw null; } init { } } public System.Func? ConstructorParameterMetadataInitializer { get { throw null; } init { } } public System.Text.Json.Serialization.JsonNumberHandling NumberHandling { get { throw null; } init { } } public System.Func? ObjectCreator { get { throw null; } init { } } @@ -1270,12 +1271,26 @@ public JsonObjectInfoValues() { } public System.Func? PropertyMetadataInitializer { get { throw null; } init { } } public System.Action? SerializeHandler { get { throw null; } init { } } } + public abstract partial class JsonParameterInfo + { + internal JsonParameterInfo() { } + public System.Type DeclaringType { get { throw null; } } + public System.Reflection.ICustomAttributeProvider? AttributeProvider { get { throw null; } } + public object? DefaultValue { get { throw null; } } + public bool HasDefaultValue { get { throw null; } } + public bool IsNullable { get { throw null; } } + public bool IsMemberInitializer { get { throw null; } } + public string Name { get { throw null; } } + public System.Type ParameterType { get { throw null; } } + public int Position { get { throw null; } } + } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class JsonParameterInfoValues { public JsonParameterInfoValues() { } public object? DefaultValue { get { throw null; } init { } } public bool HasDefaultValue { get { throw null; } init { } } + public bool IsMemberInitializer { get { throw null; } init { } } public bool IsNullable { get { throw null; } init { } } public string Name { get { throw null; } init { } } public System.Type ParameterType { get { throw null; } init { } } @@ -1293,8 +1308,10 @@ public JsonPolymorphismOptions() { } public abstract partial class JsonPropertyInfo { internal JsonPropertyInfo() { } + public System.Text.Json.Serialization.Metadata.JsonParameterInfo? AssociatedParameter { get { throw null; } } public System.Reflection.ICustomAttributeProvider? AttributeProvider { get { throw null; } set { } } public System.Text.Json.Serialization.JsonConverter? CustomConverter { get { throw null; } set { } } + public System.Type DeclaringType { get { throw null; } } public System.Func? Get { get { throw null; } set { } } public bool IsExtensionData { get { throw null; } set { } } public bool IsGetNullable { get { throw null; } set { } } @@ -1313,6 +1330,7 @@ internal JsonPropertyInfo() { } public sealed partial class JsonPropertyInfoValues { public JsonPropertyInfoValues() { } + public System.Func? AttributeProviderFactory { get; init; } public System.Text.Json.Serialization.JsonConverter? Converter { get { throw null; } init { } } public System.Type DeclaringType { get { throw null; } init { } } public System.Func? Getter { get { throw null; } init { } } @@ -1333,7 +1351,10 @@ public abstract partial class JsonTypeInfo internal JsonTypeInfo() { } public System.Text.Json.Serialization.JsonConverter Converter { get { throw null; } } public System.Func? CreateObject { get { throw null; } set { } } + public System.Reflection.ICustomAttributeProvider? ConstructorAttributeProvider { get { throw null; } } + public System.Type? ElementType { get { throw null; } } public bool IsReadOnly { get { throw null; } } + public System.Type? KeyType { get { throw null; } } public System.Text.Json.Serialization.Metadata.JsonTypeInfoKind Kind { get { throw null; } } public System.Text.Json.Serialization.JsonNumberHandling? NumberHandling { get { throw null; } set { } } public System.Action? OnDeserialized { get { throw null; } set { } } diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 773044acee233..e65049fbb87a0 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -304,7 +304,7 @@ Expected a digit ('0'-'9'), but instead reached end of data. - .NET number values such as positive and negative infinity cannot be written as valid JSON. To make it work when using 'JsonSerializer', consider specifying 'JsonNumberHandling.AllowNamedFloatingPointLiterals' (see https://docs.microsoft.com/dotnet/api/system.text.json.serialization.jsonnumberhandling). + .NET number values such as positive and negative infinity cannot be written as valid JSON. To make it work when using 'JsonSerializer', consider specifying 'JsonNumberHandling.AllowNamedFloatingPointLiterals' (see https://learn.microsoft.com/dotnet/api/system.text.json.serialization.jsonnumberhandling). The JSON value of length {0} is too large and not supported. @@ -734,8 +734,8 @@ PipeWriter.FlushAsync was canceled. - - PipeWriter has been completed, nothing more can be written to it. + + The PipeWriter '{0}' does not implement PipeWriter.UnflushedBytes. New line can be only "\n" or "\r\n". diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 9cc961c2a3b7e..f7bbf8f386f1e 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -102,7 +102,6 @@ The System.Text.Json library is built-in as part of the shared framework in .NET - diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/AsyncSerializationBufferWriterContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/AsyncSerializationBufferWriterContext.cs deleted file mode 100644 index f34bcaf612892..0000000000000 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/AsyncSerializationBufferWriterContext.cs +++ /dev/null @@ -1,85 +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.Buffers; -using System.IO; -using System.IO.Pipelines; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Text.Json.Serialization -{ - // Common interface to help de-dupe code for different types that can do async serialization (Stream and PipeWriter) - internal interface IAsyncSerializationBufferWriterContext : IDisposable - { - int FlushThreshold { get; } - - ValueTask FlushAsync(CancellationToken cancellationToken); - - public IBufferWriter BufferWriter { get; } - } - - internal readonly struct AsyncSerializationStreamContext : IAsyncSerializationBufferWriterContext - { - private readonly Stream _stream; - private readonly JsonSerializerOptions _options; - private readonly PooledByteBufferWriter _bufferWriter; - - public AsyncSerializationStreamContext(Stream stream, JsonSerializerOptions options) - { - _stream = stream; - _options = options; - _bufferWriter = new PooledByteBufferWriter(_options.DefaultBufferSize); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public async ValueTask FlushAsync(CancellationToken cancellationToken) - { - await _bufferWriter.WriteToStreamAsync(_stream, cancellationToken).ConfigureAwait(false); - _bufferWriter.Clear(); - } - - public int FlushThreshold => (int)(_options.DefaultBufferSize * JsonSerializer.FlushThreshold); - - public IBufferWriter BufferWriter => _bufferWriter; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - _bufferWriter.Dispose(); - } - } - - internal readonly struct AsyncSerializationPipeContext : IAsyncSerializationBufferWriterContext - { - private readonly PipeWriter _pipe; - - public AsyncSerializationPipeContext(PipeWriter pipe) - { - _pipe = pipe; - } - - public int FlushThreshold => (int)((4 * PipeOptions.Default.MinimumSegmentSize) * JsonSerializer.FlushThreshold); - - public IBufferWriter BufferWriter => _pipe; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public async ValueTask FlushAsync(CancellationToken cancellationToken) - { - FlushResult result = await _pipe.FlushAsync(cancellationToken).ConfigureAwait(false); - if (result.IsCanceled || result.IsCompleted) - { - if (result.IsCanceled) - { - ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled(); - } - - ThrowHelper.ThrowOperationCanceledException_PipeWriteCompleted(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() { } - } -} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs index f607b62ef3a7f..fabf97436bd26 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs @@ -15,6 +15,7 @@ internal sealed class CastingConverter : JsonConverter private readonly JsonConverter _sourceConverter; internal override Type? KeyType => _sourceConverter.KeyType; internal override Type? ElementType => _sourceConverter.ElementType; + internal override JsonConverter? NullableElementConverter => _sourceConverter.NullableElementConverter; public override bool HandleNull { get; } internal override bool SupportsCreateObjectDelegate => _sourceConverter.SupportsCreateObjectDelegate; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs index 3c189d499275b..39ec744652117 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs @@ -56,7 +56,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TElement[] array, J state.Current.EndCollectionElement(); - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.EnumeratorIndex = ++index; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs index da360f6140666..27122579aeea3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs @@ -45,7 +45,7 @@ protected internal override bool OnWriteResume( do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryOfTKeyTValueConverter.cs index dc3f9f5daf306..07341507be684 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryOfTKeyTValueConverter.cs @@ -61,7 +61,7 @@ protected internal override bool OnWriteResume( { do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs index 2351625efb3d7..9c55998ef85c0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs @@ -107,7 +107,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TAsyncEnumerable va return true; } - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { return false; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs index 9f8a48140c6be..06cb6613d816f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs @@ -60,7 +60,7 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionar do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs index 767bae403cf34..c689ed197344d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs @@ -59,7 +59,7 @@ protected override bool OnWriteResume( JsonConverter converter = GetElementConverter(ref state); do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs index 9a9c62db5708f..b32db7d002efc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs @@ -37,7 +37,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonConverter converter = GetElementConverter(ref state); do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs index aaa8fc98de513..7de440ed11fc1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs @@ -65,7 +65,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, state.Current.EndCollectionElement(); - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.EnumeratorIndex = ++index; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs index 8d7806f825b22..60a934c1104db 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs @@ -62,7 +62,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, state.Current.EndCollectionElement(); - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.EnumeratorIndex = ++index; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs index 8ca1ef71db3fb..9b0ea99ed85d3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ReadOnlyMemoryConverter.cs @@ -74,7 +74,7 @@ internal static bool OnWriteResume(Utf8JsonWriter writer, ReadOnlySpan value, state.Current.EndCollectionElement(); - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.EnumeratorIndex = ++index; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 7831bb5fdc873..aeca4e87a23c3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -59,7 +59,7 @@ protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection JsonConverter converter = GetElementConverter(ref state); do { - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { state.Current.CollectionEnumerator = enumerator; return false; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs index 8ac1b709b8b91..905da12217480 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpOptionConverter.cs @@ -12,6 +12,7 @@ internal sealed class FSharpOptionConverter : JsonConverter typeof(TElement); + internal override JsonConverter? NullableElementConverter => _elementConverter; // 'None' is encoded using 'null' at runtime and serialized as 'null' in JSON. public override bool HandleNull => true; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs index 8f63a705a6892..b74d89a01a1c8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpValueOptionConverter.cs @@ -12,6 +12,7 @@ internal sealed class FSharpValueOptionConverter : JsonC where TValueOption : struct, IEquatable { internal override Type? ElementType => typeof(TElement); + internal override JsonConverter? NullableElementConverter => _elementConverter; // 'ValueNone' is encoded using 'default' at runtime and serialized as 'null' in JSON. public override bool HandleNull => true; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs index 88303c6284d1f..7808ca49443de 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs @@ -20,6 +20,7 @@ internal sealed class JsonMetadataServicesConverter : JsonResumableConverter< internal override Type? KeyType => Converter.KeyType; internal override Type? ElementType => Converter.ElementType; + internal override JsonConverter? NullableElementConverter => Converter.NullableElementConverter; public override bool HandleNull { get; } internal override bool ConstructorIsParameterized => Converter.ConstructorIsParameterized; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/JsonObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/JsonObjectConverter.cs index 0a4bb183178db..3dd6f6319e744 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/JsonObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/JsonObjectConverter.cs @@ -11,6 +11,5 @@ internal abstract class JsonObjectConverter : JsonResumableConverter { private protected sealed override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Object; internal override bool CanPopulate => true; - internal sealed override Type? ElementType => null; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index bb47eeba0e20f..b8935e1f2b478 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -403,7 +403,7 @@ internal sealed override bool OnTryWrite( state.Current.EndProperty(); state.Current.EnumeratorIndex++; - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { return false; } @@ -432,7 +432,7 @@ internal sealed override bool OnTryWrite( state.Current.EndProperty(); state.Current.EnumeratorIndex++; - if (ShouldFlush(writer, ref state)) + if (ShouldFlush(ref state, writer)) { return false; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs index f435febc7f393..7a16b52e87ea7 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs @@ -61,7 +61,7 @@ protected sealed override void InitializeConstructorArgumentCaches(ref ReadStack object?[] arguments = ArrayPool.Shared.Rent(typeInfo.ParameterCache.Count); foreach (JsonParameterInfo parameterInfo in typeInfo.ParameterCache) { - arguments[parameterInfo.Position] = parameterInfo.DefaultValue; + arguments[parameterInfo.Position] = parameterInfo.EffectiveDefaultValue; } state.Current.CtorArgumentState!.Arguments = arguments; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs index 0179dc68310fc..9176c2847697b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs @@ -73,7 +73,7 @@ private static bool TryRead( if (info.IgnoreNullTokensOnRead) { // Use default value specified on parameter, if any. - value = info.DefaultValue; + value = info.EffectiveDefaultValue; } else if (!info.IsNullable && info.Options.RespectNullableAnnotations) { @@ -102,16 +102,16 @@ protected override void InitializeConstructorArgumentCaches(ref ReadStack state, switch (parameterInfo.Position) { case 0: - arguments.Arg0 = ((JsonParameterInfo)parameterInfo).DefaultValue; + arguments.Arg0 = ((JsonParameterInfo)parameterInfo).EffectiveDefaultValue; break; case 1: - arguments.Arg1 = ((JsonParameterInfo)parameterInfo).DefaultValue; + arguments.Arg1 = ((JsonParameterInfo)parameterInfo).EffectiveDefaultValue; break; case 2: - arguments.Arg2 = ((JsonParameterInfo)parameterInfo).DefaultValue; + arguments.Arg2 = ((JsonParameterInfo)parameterInfo).EffectiveDefaultValue; break; case 3: - arguments.Arg3 = ((JsonParameterInfo)parameterInfo).DefaultValue; + arguments.Arg3 = ((JsonParameterInfo)parameterInfo).EffectiveDefaultValue; break; default: Debug.Fail("More than 4 params: we should be in override for LargeObjectWithParameterizedConstructorConverter."); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs index 3d6ca980e11c0..34cf9809cc087 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs @@ -610,7 +610,7 @@ protected static bool TryLookupConstructorParameter( // For case insensitive and missing property support of JsonPath, remember the value on the temporary stack. state.Current.JsonPropertyName = utf8PropertyName; - jsonParameterInfo = jsonPropertyInfo.ParameterInfo; + jsonParameterInfo = jsonPropertyInfo.AssociatedParameter; if (jsonParameterInfo != null) { state.Current.JsonPropertyInfo = null; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs index 7bf8b73359a5e..d5803e65b2cc8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/NullableConverter.cs @@ -8,6 +8,7 @@ namespace System.Text.Json.Serialization.Converters internal sealed class NullableConverter : JsonConverter where T : struct { internal override Type? ElementType => typeof(T); + internal override JsonConverter? NullableElementConverter => _elementConverter; public override bool HandleNull => true; internal override bool CanPopulate => _elementConverter.CanPopulate; internal override bool ConstructorIsParameterized => _elementConverter.ConstructorIsParameterized; @@ -21,7 +22,6 @@ public NullableConverter(JsonConverter elementConverter) _elementConverter = elementConverter; IsInternalConverterForNumberType = elementConverter.IsInternalConverterForNumberType; ConverterStrategy = elementConverter.ConverterStrategy; - ConstructorInfo = elementConverter.ConstructorInfo; } internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index 1acdf4b1e9226..8c61188630276 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -152,9 +152,11 @@ internal JsonConverter CreateCastingConverter() /// internal virtual JsonConverter? SourceConverterForCastingConverter => null; - internal abstract Type? ElementType { get; } + internal virtual Type? ElementType => null; - internal abstract Type? KeyType { get; } + internal virtual Type? KeyType => null; + + internal virtual JsonConverter? NullableElementConverter => null; /// /// Cached value of TypeToConvert.IsValueType, which is an expensive call. @@ -171,10 +173,17 @@ internal JsonConverter CreateCastingConverter() /// internal bool IsInternalConverterForNumberType { get; init; } - internal static bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state) + internal static bool ShouldFlush(ref WriteStack state, Utf8JsonWriter writer) { - // If surpassed flush threshold then return false which will flush stream. - return (state.FlushThreshold > 0 && writer.BytesPending > state.FlushThreshold); + Debug.Assert(state.FlushThreshold == 0 || (state.PipeWriter is { CanGetUnflushedBytes: true }), + "ShouldFlush should only be called by resumable serializers, all of which use the PipeWriter abstraction with CanGetUnflushedBytes == true."); + // If surpassed flush threshold then return true which will flush stream. + if (state.PipeWriter is { } pipeWriter) + { + return state.FlushThreshold > 0 && pipeWriter.UnflushedBytes > state.FlushThreshold - writer.BytesPending; + } + + return false; } internal abstract object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs index 7537fa0d833be..4ab1eca5b1a5d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs @@ -32,10 +32,6 @@ protected JsonConverterFactory() { } /// public abstract JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options); - internal sealed override Type? KeyType => null; - - internal sealed override Type? ElementType => null; - internal JsonConverter GetConverterInternal(Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(CanConvert(typeToConvert)); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index cf8b87696ea71..cce660169ac28 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -57,10 +57,6 @@ internal sealed override JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions o return new JsonTypeInfo(this, options); } - internal override Type? KeyType => null; - - internal override Type? ElementType => null; - /// /// Indicates whether should be passed to the converter on serialization, /// and whether should be passed on deserialization. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index a6b71ea822077..05ae6cec8721a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -13,6 +13,9 @@ namespace System.Text.Json.Serialization.Metadata { public partial class DefaultJsonTypeInfoResolver { + private static readonly bool s_isNullabilityInfoContextSupported = + AppContext.TryGetSwitch("System.Reflection.NullabilityInfoContext.IsSupported", out bool isSupported) ? isSupported : true; + internal static MemberAccessor MemberAccessor { [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] @@ -69,9 +72,12 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte typeInfo.SetCreateObjectIfCompatible(createObject); typeInfo.CreateObjectForExtensionDataProperty = createObject; - if (typeInfo.Kind is JsonTypeInfoKind.Object) + if (typeInfo is { Kind: JsonTypeInfoKind.Object, IsNullable: false }) { - NullabilityInfoContext nullabilityCtx = new(); + // If the System.Reflection.NullabilityInfoContext.IsSupported feature switch has been disabled, + // we want to avoid resolving nullability information for properties and parameters unless the + // user has explicitly opted into nullability enforcement in which case an exception will be surfaced. + NullabilityInfoContext? nullabilityCtx = s_isNullabilityInfoContextSupported || options.RespectNullableAnnotations ? new() : null; if (converter.ConstructorIsParameterized) { @@ -81,6 +87,8 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte } PopulateProperties(typeInfo, nullabilityCtx); + + typeInfo.ConstructorAttributeProvider = typeInfo.Converter.ConstructorInfo; } // Plug in any converter configuration -- should be run last. @@ -91,7 +99,7 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx) + private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoContext? nullabilityCtx) { Debug.Assert(!typeInfo.IsReadOnly); Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); @@ -132,33 +140,13 @@ private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoCon BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - /// - /// Looks up the type for a member matching the given name and member type. - /// - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - internal static MemberInfo? LookupMemberInfo(Type type, MemberTypes memberType, string name) - { - Debug.Assert(memberType is MemberTypes.Field or MemberTypes.Property); - - // Walk the type hierarchy starting from the current type up to the base type(s) - foreach (Type t in type.GetSortedTypeHierarchy()) - { - MemberInfo[] members = t.GetMember(name, memberType, AllInstanceMembers); - if (members.Length > 0) - { - return members[0]; - } - } - - return null; - } [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static void AddMembersDeclaredBySuperType( JsonTypeInfo typeInfo, Type currentType, - NullabilityInfoContext nullabilityCtx, + NullabilityInfoContext? nullabilityCtx, bool constructorHasSetsRequiredMembersAttribute, ref JsonTypeInfo.PropertyHierarchyResolutionState state) { @@ -219,7 +207,7 @@ private static void AddMember( JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, - NullabilityInfoContext nullabilityCtx, + NullabilityInfoContext? nullabilityCtx, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute, ref JsonTypeInfo.PropertyHierarchyResolutionState state) @@ -241,7 +229,7 @@ private static void AddMember( JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, - NullabilityInfoContext nullabilityCtx, + NullabilityInfoContext? nullabilityCtx, JsonSerializerOptions options, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute) @@ -301,7 +289,7 @@ private static bool PropertyIsOverriddenAndIgnored(PropertyInfo propertyInfo, Di [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx) + private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext? nullabilityCtx) { Debug.Assert(typeInfo.Converter.ConstructorInfo != null); ParameterInfo[] parameters = typeInfo.Converter.ConstructorInfo.GetParameters(); @@ -326,9 +314,7 @@ private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, Nullabili Position = reflectionInfo.Position, HasDefaultValue = reflectionInfo.HasDefaultValue, DefaultValue = reflectionInfo.GetDefaultValue(), - IsNullable = - reflectionInfo.ParameterType.IsNullableType() && - DetermineParameterNullability(reflectionInfo, nullabilityCtx) is not NullabilityState.NotNull, + IsNullable = DetermineParameterNullability(reflectionInfo, nullabilityCtx) is not NullabilityState.NotNull, }; jsonParameters[i] = jsonInfo; @@ -344,7 +330,7 @@ private static void PopulatePropertyInfo( MemberInfo memberInfo, JsonConverter? customConverter, JsonIgnoreCondition? ignoreCondition, - NullabilityInfoContext nullabilityCtx, + NullabilityInfoContext? nullabilityCtx, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute) { @@ -487,9 +473,9 @@ internal static void DeterminePropertyAccessors(JsonPropertyInfo jsonPrope [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, NullabilityInfoContext nullabilityCtx) + private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, NullabilityInfoContext? nullabilityCtx) { - if (!propertyInfo.PropertyTypeCanBeNull) + if (!propertyInfo.PropertyTypeCanBeNull || nullabilityCtx is null) { return; } @@ -511,8 +497,17 @@ private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContext nullabilityCtx) + private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContext? nullabilityCtx) { + if (!parameterInfo.ParameterType.IsNullableType()) + { + return NullabilityState.NotNull; + } + + if (nullabilityCtx is null) + { + return NullabilityState.Unknown; + } #if NET8_0 // Workaround for https://github.com/dotnet/runtime/issues/92487 // The fix has been incorporated into .NET 9 (and the polyfilled implementations in netfx). diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs index b9cf70297f1ab..3b5648eddd024 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs @@ -53,6 +53,7 @@ private static JsonTypeInfo CreateCore(JsonSerializerOptions options, Json typeInfo.PropertyMetadataSerializationNotSupported = true; } + typeInfo.ConstructorAttributeProviderFactory = objectInfo.ConstructorAttributeProviderFactory; typeInfo.SerializeHandler = objectInfo.SerializeHandler; typeInfo.NumberHandling = objectInfo.NumberHandling; typeInfo.PopulatePolymorphismMetadata(); @@ -195,7 +196,7 @@ private static JsonPropertyInfo CreatePropertyInfoCore(JsonPropertyInfoVal propertyInfo.IgnoreCondition = propertyInfoValues.IgnoreCondition; propertyInfo.JsonTypeInfo = propertyInfoValues.PropertyTypeInfo; propertyInfo.NumberHandling = propertyInfoValues.NumberHandling; - propertyInfo.IsSourceGenerated = true; + propertyInfo.AttributeProviderFactory = propertyInfoValues.AttributeProviderFactory; return propertyInfo; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs index 29022e6947dcd..5e704a2be7e05 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonObjectInfoValuesOfT.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Reflection; namespace System.Text.Json.Serialization.Metadata { @@ -37,6 +38,11 @@ public sealed class JsonObjectInfoValues /// This API is for use by the output of the System.Text.Json source generator and should not be called directly. public Func? ConstructorParameterMetadataInitializer { get; init; } + /// + /// Provides a delayed attribute provider corresponding to the deserialization constructor. + /// + public Func? ConstructorAttributeProviderFactory { get; init; } + /// /// Specifies how number properties and fields should be processed when serializing and deserializing. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs index 3f898a21b9a3a..6adb7a7d8fed3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfo.cs @@ -2,14 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; namespace System.Text.Json.Serialization.Metadata { /// - /// Holds relevant state about a method parameter, like the default value of - /// the parameter, and the position in the method's parameter list. + /// Provides JSON serialization-related metadata about a constructor parameter. /// - internal abstract class JsonParameterInfo + public abstract class JsonParameterInfo { internal JsonParameterInfo(JsonParameterInfoValues parameterInfoValues, JsonPropertyInfo matchingProperty) { @@ -18,29 +19,109 @@ internal JsonParameterInfo(JsonParameterInfoValues parameterInfoValues, JsonProp Position = parameterInfoValues.Position; Name = parameterInfoValues.Name; HasDefaultValue = parameterInfoValues.HasDefaultValue; - IsNullable = parameterInfoValues.IsNullable; + DefaultValue = parameterInfoValues.HasDefaultValue ? parameterInfoValues.DefaultValue : null; MatchingProperty = matchingProperty; + IsMemberInitializer = parameterInfoValues.IsMemberInitializer; } + /// + /// Gets the declaring type of the constructor. + /// + public Type DeclaringType => MatchingProperty.DeclaringType; + + /// + /// Gets the zero-based position of the parameter in the formal parameter list. + /// public int Position { get; } + + /// + /// Gets the type of this parameter. + /// + public Type ParameterType => MatchingProperty.PropertyType; + + /// + /// Gets the name of the parameter. + /// public string Name { get; } + + /// + /// Gets a value indicating whether the parameter has a default value. + /// public bool HasDefaultValue { get; } - // The default value of the parameter. This is `DefaultValue` of the `ParameterInfo`, if specified, or the `default` for the `ParameterType`. - public object? DefaultValue { get; private protected init; } - public JsonPropertyInfo MatchingProperty { get; } - public bool IsNullable { get; internal set; } + /// + /// Gets a value indicating the default value if the parameter has a default value. + /// + public object? DefaultValue { get; } - public Type DeclaringType => MatchingProperty.DeclaringType; - public Type ParameterType => MatchingProperty.PropertyType; - public JsonConverter EffectiveConverter => MatchingProperty.EffectiveConverter; - public bool IgnoreNullTokensOnRead => MatchingProperty.IgnoreNullTokensOnRead; - public JsonSerializerOptions Options => MatchingProperty.Options; + /// + /// The default value to be passed to the constructor argument array, replacing null with default(TParameter). + /// + internal object? EffectiveDefaultValue { get; private protected init; } + + /// + /// Gets a value indicating whether the constructor parameter is annotated as nullable. + /// + /// + /// Contracts originating from or , + /// derive the value of this parameter from nullable reference type annotations, including annotations + /// from attributes such as or . + /// + /// This property has no effect on deserialization unless the + /// property has been enabled, in which case the serializer will reject any deserialization results. + /// + /// This setting is in sync with the associated property. + /// + public bool IsNullable => MatchingProperty.IsSetNullable; + + /// + /// Gets a value indicating whether the parameter represents a required or init-only member initializer. + /// + /// + /// Only returns for source generated metadata which can only access + /// required or init-only member initializers using object initialize expressions. + /// + public bool IsMemberInitializer { get; } + + /// + /// Gets a custom attribute provider for the current parameter. + /// + /// + /// When resolving metadata via the built-in resolvers this will be populated with + /// the underlying of the constructor metadata. + /// + public ICustomAttributeProvider? AttributeProvider + { + get + { + // Use delayed initialization to ensure that reflection dependencies are pay-for-play. + Debug.Assert(MatchingProperty.DeclaringTypeInfo != null, "Declaring type metadata must have already been configured."); + ICustomAttributeProvider? parameterInfo = _attributeProvider; + if (parameterInfo is null && MatchingProperty.DeclaringTypeInfo.ConstructorAttributeProvider is MethodBase ctorInfo) + { + ParameterInfo[] parameters = ctorInfo.GetParameters(); + if (Position < parameters.Length) + { + _attributeProvider = parameterInfo = parameters[Position]; + } + } + + return parameterInfo; + } + } + + private ICustomAttributeProvider? _attributeProvider; + + internal JsonPropertyInfo MatchingProperty { get; } + + internal JsonConverter EffectiveConverter => MatchingProperty.EffectiveConverter; + internal bool IgnoreNullTokensOnRead => MatchingProperty.IgnoreNullTokensOnRead; + internal JsonSerializerOptions Options => MatchingProperty.Options; // The effective name of the parameter as UTF-8 bytes. - public byte[] JsonNameAsUtf8Bytes => MatchingProperty.NameAsUtf8Bytes; - public JsonNumberHandling? NumberHandling => MatchingProperty.EffectiveNumberHandling; - public JsonTypeInfo JsonTypeInfo => MatchingProperty.JsonTypeInfo; - public bool ShouldDeserialize => !MatchingProperty.IsIgnored; + internal byte[] JsonNameAsUtf8Bytes => MatchingProperty.NameAsUtf8Bytes; + internal JsonNumberHandling? NumberHandling => MatchingProperty.EffectiveNumberHandling; + internal JsonTypeInfo JsonTypeInfo => MatchingProperty.JsonTypeInfo; + internal bool ShouldDeserialize => !MatchingProperty.IsIgnored; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs index 273f86eb31666..342fddecd2270 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoOfT.cs @@ -13,19 +13,21 @@ internal sealed class JsonParameterInfo : JsonParameterInfo { public new JsonConverter EffectiveConverter => MatchingProperty.EffectiveConverter; public new JsonPropertyInfo MatchingProperty { get; } - public new T? DefaultValue { get; } + public new T? EffectiveDefaultValue { get; } public JsonParameterInfo(JsonParameterInfoValues parameterInfoValues, JsonPropertyInfo matchingPropertyInfo) : base(parameterInfoValues, matchingPropertyInfo) { Debug.Assert(parameterInfoValues.ParameterType == typeof(T)); + Debug.Assert(!matchingPropertyInfo.IsConfigured); - MatchingProperty = matchingPropertyInfo; - DefaultValue = parameterInfoValues.HasDefaultValue && parameterInfoValues.DefaultValue is not null - ? (T)parameterInfoValues.DefaultValue - : default; + if (parameterInfoValues is { HasDefaultValue: true, DefaultValue: object defaultValue }) + { + EffectiveDefaultValue = (T)defaultValue; + } - base.DefaultValue = DefaultValue; + MatchingProperty = matchingPropertyInfo; + base.EffectiveDefaultValue = EffectiveDefaultValue; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoValues.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoValues.cs index 24ad156c6a60d..26d0b2199a389 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoValues.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonParameterInfoValues.cs @@ -47,5 +47,11 @@ public sealed class JsonParameterInfoValues /// /// This API is for use by the output of the System.Text.Json source generator and should not be called directly. public bool IsNullable { get; init; } + + /// + /// Whether the parameter represents a required or init-only member initializer. + /// + /// This API is for use by the output of the System.Text.Json source generator and should not be called directly. + public bool IsMemberInitializer { get; init; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs index 696a6dee1e7d5..6a805e80162bf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs @@ -155,7 +155,7 @@ internal JsonIgnoreCondition? IgnoreCondition /// The instance has been locked for further modification. /// /// - /// When resolving metadata via this + /// When resolving metadata via the built-in resolvers this /// will be populated with the underlying of the serialized property or field. /// /// Setting a custom attribute provider will have no impact on the contract model, @@ -165,37 +165,30 @@ public ICustomAttributeProvider? AttributeProvider { get { - ICustomAttributeProvider attributeProvider = _attributeProvider ?? InitializeAttributeProvider(); - return ReferenceEquals(attributeProvider, s_nullAttributeProvider) ? null : attributeProvider; + Func? attributeProviderFactory = Volatile.Read(ref AttributeProviderFactory); + ICustomAttributeProvider? attributeProvider = _attributeProvider; + + if (attributeProvider is null && attributeProviderFactory is not null) + { + _attributeProvider = attributeProvider = attributeProviderFactory(); + Volatile.Write(ref AttributeProviderFactory, null); + } + + return attributeProvider; } set { VerifyMutable(); - _attributeProvider = value ?? s_nullAttributeProvider; + _attributeProvider = value; + Volatile.Write(ref AttributeProviderFactory, null); } } - // Because the getter can initialize its own backing field, we want to avoid races between the getter and setter. - // This is done using CAS on the single _attributeProvider field which employs the following encoding: - // null: not initialized, s_nullAttributeProvider: null, otherwise: _attributeProvider + // Metadata emanating from the source generator use delayed attribute provider initialization + // ensuring that reflection metadata resolution remains pay-for-play and is trimmable. + internal Func? AttributeProviderFactory; private ICustomAttributeProvider? _attributeProvider; - private static readonly ICustomAttributeProvider s_nullAttributeProvider = typeof(NullAttributeProviderPlaceholder); - private sealed class NullAttributeProviderPlaceholder; - - [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", - Justification = "Looks up members that are already being referenced by the source generator.")] - private ICustomAttributeProvider InitializeAttributeProvider() - { - // If the property is source generated, perform a reflection lookup of its MemberInfo. - // Avoids overhead of reflection at startup and makes this method trimmable if unused. - ICustomAttributeProvider? provider = IsSourceGenerated && MemberName != null - ? DefaultJsonTypeInfoResolver.LookupMemberInfo(DeclaringType, MemberType, MemberName) - : null; - - provider ??= s_nullAttributeProvider; - return Interlocked.CompareExchange(ref _attributeProvider, provider, null) ?? provider; - } /// /// Gets or sets a value indicating if the property or field should be replaced or populated during deserialization. @@ -231,7 +224,6 @@ public JsonObjectCreationHandling? ObjectCreationHandling internal string? MemberName { get; set; } internal MemberTypes MemberType { get; set; } internal bool IsVirtual { get; set; } - internal bool IsSourceGenerated { get; set; } /// /// Gets or sets a value indicating whether the return type of the getter is annotated as nullable. @@ -302,17 +294,11 @@ public bool IsSetNullable ThrowHelper.ThrowInvalidOperationException_PropertyTypeNotNullable(this); } - if (ParameterInfo != null) - { - // Ensure the new setting is reflected in the associated parameter. - ParameterInfo.IsNullable = value; - } - _isSetNullable = value; } } - private bool _isSetNullable; + private protected bool _isSetNullable; /// /// Specifies whether the current property is a special extension data property. @@ -372,7 +358,18 @@ public bool IsRequired private bool _isRequired; - internal JsonParameterInfo? ParameterInfo { get; private set; } + /// + /// Gets the constructor parameter associated with the current property. + /// + /// + /// Returns the metadata for the parameter in the + /// deserialization constructor that has been associated with the current property. + /// + /// A constructor parameter is matched to a property or field if they are of the + /// same type and have the same name, up to case insensitivity. Each constructor + /// parameter must be matched to exactly one property of field. + /// + public JsonParameterInfo? AssociatedParameter { get; internal set; } internal JsonPropertyInfo(Type declaringType, Type propertyType, JsonTypeInfo? declaringTypeInfo, JsonSerializerOptions options) { @@ -399,6 +396,11 @@ internal static JsonPropertyInfo GetPropertyPlaceholder() return info; } + /// + /// Gets the declaring type of the property. + /// + public Type DeclaringType { get; } + /// /// Gets the type of the current property. /// @@ -676,19 +678,6 @@ private void DetermineEffectiveObjectCreationHandlingForProperty() EffectiveObjectCreationHandling = effectiveObjectCreationHandling; } - private void DetermineParameterInfo() - { - Debug.Assert(DeclaringTypeInfo?.IsConfigured is false); - ParameterInfo = DeclaringTypeInfo.CreateMatchingParameterInfo(this); - if (ParameterInfo != null) - { - // Given that we have associated a constructor parameter to this property, - // deserialization is no longer governed by the property setter. - // Ensure nullability configuration is copied over from the parameter to the property. - _isSetNullable = ParameterInfo.IsNullable; - } - } - private bool NumberHandingIsApplicable() { if (EffectiveConverter.IsInternalConverterForNumberType) @@ -734,7 +723,7 @@ private bool NumberHandingIsApplicable() /// /// Creates a instance whose type matches that of the current property. /// - internal abstract JsonParameterInfo CreateJsonParameterInfo(JsonParameterInfoValues parameterInfoValues); + internal abstract void AddJsonParameterInfo(JsonParameterInfoValues parameterInfoValues); internal abstract bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer); internal abstract bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteStack state, Utf8JsonWriter writer); @@ -949,7 +938,7 @@ internal void EnsureChildOf(JsonTypeInfo parent) ThrowHelper.ThrowInvalidOperationException_JsonPropertyInfoIsBoundToDifferentJsonTypeInfo(this); } - DetermineParameterInfo(); + DeclaringTypeInfo.ResolveMatchingParameterInfo(this); } /// @@ -970,8 +959,6 @@ internal bool TryGetPrePopulatedValue(scoped ref ReadStack state) return value != null; } - internal Type DeclaringType { get; } - internal JsonTypeInfo JsonTypeInfo { get @@ -1054,7 +1041,7 @@ public JsonNumberHandling? NumberHandling /// /// Number handling after considering options and declaring type number handling /// - internal JsonNumberHandling? EffectiveNumberHandling { get; set; } + internal JsonNumberHandling? EffectiveNumberHandling { get; private set; } // Whether the property type can be null. internal abstract bool PropertyTypeCanBeNull { get; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs index ff38badacdc59..ff31cf97a353d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs @@ -112,8 +112,15 @@ private protected override void SetShouldSerialize(Delegate? predicate) internal override object? DefaultValue => default(T); internal override bool PropertyTypeCanBeNull => default(T) is null; - internal override JsonParameterInfo CreateJsonParameterInfo(JsonParameterInfoValues parameterInfoValues) - => new JsonParameterInfo(parameterInfoValues, this); + internal override void AddJsonParameterInfo(JsonParameterInfoValues parameterInfoValues) + { + Debug.Assert(!IsConfigured); + Debug.Assert(AssociatedParameter is null); + + AssociatedParameter = new JsonParameterInfo(parameterInfoValues, this); + // Overwrite the nullability annotation of property setter with the parameter. + _isSetNullable = parameterInfoValues.IsNullable; + } internal new JsonConverter EffectiveConverter { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs index 624879e469051..78cb0e6ff902d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoValuesOfT.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Reflection; namespace System.Text.Json.Serialization.Metadata { @@ -81,5 +82,10 @@ public sealed class JsonPropertyInfoValues /// The name to be used when processing the property or field, specified by . /// public string? JsonPropertyName { get; init; } + + /// + /// Provides a factory that maps to . + /// + public Func? AttributeProviderFactory { get; init; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs index 297b9f0cb2dde..34860fdfefb8d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs @@ -27,7 +27,7 @@ public abstract partial class JsonTypeInfo // The number of parameters the deserialization constructor has. If this is not equal to ParameterCache.Count, this means // that not all parameters are bound to object properties, and an exception will be thrown if deserialization is attempted. - internal int ParameterCount { get; private set; } + internal int ParameterCount { get; private protected set; } // All of the serializable parameters on a POCO constructor keyed on parameter name. // Only parameters which bind to properties are cached. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 71d47aab99441..41431ee40e3e1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Pipelines; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Text.Json.Reflection; @@ -49,6 +50,27 @@ internal JsonTypeInfo(Type type, JsonConverter converter, JsonSerializerOptions KeyType = converter.KeyType; } + /// + /// Gets the element type corresponding to an enumerable, dictionary or optional type. + /// + /// + /// Returns the element type for enumerable types, the value type for dictionary types, + /// and the underlying type for or F# optional types. + /// + /// Returns for all other types or types using custom converters. + /// + public Type? ElementType { get; } + + /// + /// Gets the key type corresponding to a dictionary type. + /// + /// + /// Returns the key type for dictionary types. + /// + /// Returns for all other types or types using custom converters. + /// + public Type? KeyType { get; } + /// /// Gets or sets a parameterless factory to be used on deserialization. /// @@ -337,6 +359,8 @@ public JsonPolymorphismOptions? PolymorphismOptions // so it is allowed to be used for fast-path serialization but it will throw if used for metadata-based serialization internal bool PropertyMetadataSerializationNotSupported { get; set; } + internal bool IsNullable => Converter.NullableElementConverter is not null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ValidateCanBeUsedForPropertyMetadataSerialization() { @@ -346,9 +370,6 @@ internal void ValidateCanBeUsedForPropertyMetadataSerialization() } } - internal Type? ElementType { get; } - internal Type? KeyType { get; } - /// /// Return the JsonTypeInfo for the element type, or null if the type is not an enumerable or dictionary. /// @@ -437,7 +458,7 @@ internal JsonTypeInfo? KeyTypeInfo /// User-defined custom converters (specified either via or ) /// are metadata-agnostic and thus always resolve to . /// - public JsonTypeInfoKind Kind { get; private set; } + public JsonTypeInfoKind Kind { get; } /// /// Dummy instance corresponding to the declaring type of this . @@ -599,6 +620,45 @@ public IJsonTypeInfoResolver? OriginatingResolver private IJsonTypeInfoResolver? _originatingResolver; + /// + /// Gets or sets an attribute provider corresponding to the deserialization constructor. + /// + /// + /// The instance has been locked for further modification. + /// + /// + /// When resolving metadata via the built-in resolvers this will be populated with + /// the underlying of the serialized property or field. + /// + public ICustomAttributeProvider? ConstructorAttributeProvider + { + get + { + Func? ctorAttrProviderFactory = Volatile.Read(ref ConstructorAttributeProviderFactory); + ICustomAttributeProvider? ctorAttrProvider = _constructorAttributeProvider; + + if (ctorAttrProvider is null && ctorAttrProviderFactory is not null) + { + _constructorAttributeProvider = ctorAttrProvider = ctorAttrProviderFactory(); + Volatile.Write(ref ConstructorAttributeProviderFactory, null); + } + + return ctorAttrProvider; + } + internal set + { + Debug.Assert(!IsReadOnly); + + _constructorAttributeProvider = value; + Volatile.Write(ref ConstructorAttributeProviderFactory, null); + } + } + + // Metadata emanating from the source generator use delayed attribute provider initialization + // ensuring that reflection metadata resolution remains pay-for-play and is trimmable. + internal Func? ConstructorAttributeProviderFactory; + private ICustomAttributeProvider? _constructorAttributeProvider; + internal void VerifyMutable() { if (IsReadOnly) @@ -986,12 +1046,11 @@ public JsonPropertyInfo CreateJsonPropertyInfo(Type propertyType, string name) return propertyInfo; } - private Dictionary? _parameterInfoValuesIndex; + private protected Dictionary? _parameterInfoValuesIndex; // Untyped, root-level serialization methods internal abstract void SerializeAsObject(Utf8JsonWriter writer, object? rootValue); - internal abstract Task SerializeAsObjectAsync(TSerializationContext serializationContext, object? rootValue, CancellationToken cancellationToken) - where TSerializationContext : struct, IAsyncSerializationBufferWriterContext; + internal abstract Task SerializeAsObjectAsync(PipeWriter pipeWriter, object? rootValue, int flushThreshold, CancellationToken cancellationToken); internal abstract Task SerializeAsObjectAsync(Stream utf8Json, object? rootValue, CancellationToken cancellationToken); internal abstract Task SerializeAsObjectAsync(PipeWriter utf8Json, object? rootValue, CancellationToken cancellationToken); internal abstract void SerializeAsObject(Stream utf8Json, object? rootValue); @@ -1008,7 +1067,7 @@ internal ref struct PropertyHierarchyResolutionState(JsonSerializerOptions optio public bool IsPropertyOrderSpecified; } - private readonly struct ParameterLookupKey(Type type, string name) : IEquatable + private protected readonly struct ParameterLookupKey(Type type, string name) : IEquatable { public Type Type { get; } = type; public string Name { get; } = name; @@ -1106,25 +1165,23 @@ internal void PopulateParameterInfoValues(JsonParameterInfoValues[] parameterInf _parameterInfoValuesIndex = parameterIndex; } - internal JsonParameterInfo? CreateMatchingParameterInfo(JsonPropertyInfo propertyInfo) + internal void ResolveMatchingParameterInfo(JsonPropertyInfo propertyInfo) { Debug.Assert( - !Converter.ConstructorIsParameterized || _parameterInfoValuesIndex is not null, + CreateObjectWithArgs is null || _parameterInfoValuesIndex is not null, "Metadata with parameterized constructors must have populated parameter info metadata."); if (_parameterInfoValuesIndex is not { } index) { - return null; + return; } string propertyName = propertyInfo.MemberName ?? propertyInfo.Name; ParameterLookupKey propKey = new(propertyInfo.PropertyType, propertyName); if (index.TryGetValue(propKey, out JsonParameterInfoValues? matchingParameterInfoValues)) { - return propertyInfo.CreateJsonParameterInfo(matchingParameterInfoValues); + propertyInfo.AddJsonParameterInfo(matchingParameterInfoValues); } - - return null; } internal void ConfigureConstructorParameters() @@ -1140,7 +1197,7 @@ internal void ConfigureConstructorParameters() foreach (KeyValuePair kvp in PropertyCache.List) { JsonPropertyInfo propertyInfo = kvp.Value; - JsonParameterInfo? parameterInfo = propertyInfo.ParameterInfo; + JsonParameterInfo? parameterInfo = propertyInfo.AssociatedParameter; if (parameterInfo is null) { continue; @@ -1160,7 +1217,7 @@ internal void ConfigureConstructorParameters() parameterCache.Add(parameterInfo); } - if (ExtensionDataProperty is { ParameterInfo: not null }) + if (ExtensionDataProperty is { AssociatedParameter: not null }) { Debug.Assert(ExtensionDataProperty.MemberName != null, "Custom property info cannot be data extension property"); ThrowHelper.ThrowInvalidOperationException_ExtensionDataCannotBindToCtorParam(ExtensionDataProperty.MemberName, ExtensionDataProperty); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs index a5c2bf8745592..949200f22d849 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs @@ -64,7 +64,10 @@ internal Task SerializeAsync(Stream utf8Json, CancellationToken cancellationToken, object? rootValueBoxed = null) { - return SerializeAsync(new AsyncSerializationStreamContext(utf8Json, Options), rootValue, cancellationToken, rootValueBoxed); + // Value chosen as 90% of the default buffer used in PooledByteBufferWriter. + // This is a tradeoff between likelihood of needing to grow the array vs. utilizing most of the buffer + int flushThreshold = (int)(Options.DefaultBufferSize * JsonSerializer.FlushThreshold); + return SerializeAsync(new PooledByteBufferWriter(Options.DefaultBufferSize, utf8Json), rootValue, flushThreshold, cancellationToken, rootValueBoxed); } internal Task SerializeAsync(PipeWriter utf8Json, @@ -72,15 +75,18 @@ internal Task SerializeAsync(PipeWriter utf8Json, CancellationToken cancellationToken, object? rootValueBoxed = null) { - return SerializeAsync(new AsyncSerializationPipeContext(utf8Json), rootValue, cancellationToken, rootValueBoxed); + // Value chosen as 90% of 4 buffer segments in Pipes. This is semi-arbitrarily chosen and may be changed in future iterations. + int flushThreshold = (int)(4 * PipeOptions.Default.MinimumSegmentSize * JsonSerializer.FlushThreshold); + return SerializeAsync(utf8Json, rootValue, flushThreshold, cancellationToken, rootValueBoxed); } // Root serialization method for async streaming serialization. - private async Task SerializeAsync( - TSerializationContext serializationContext, + private async Task SerializeAsync( + PipeWriter pipeWriter, T? rootValue, + int flushThreshold, CancellationToken cancellationToken, - object? rootValueBoxed = null) where TSerializationContext : struct, IAsyncSerializationBufferWriterContext + object? rootValueBoxed = null) { Debug.Assert(IsConfigured); Debug.Assert(rootValueBoxed is null || rootValueBoxed is T); @@ -93,7 +99,7 @@ private async Task SerializeAsync( Debug.Assert(CanUseSerializeHandler); Debug.Assert(Converter is JsonMetadataServicesConverter); - Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriter(Options, serializationContext.BufferWriter); + Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriter(Options, pipeWriter); try { @@ -111,11 +117,21 @@ private async Task SerializeAsync( Utf8JsonWriterCache.ReturnWriter(writer); } - await serializationContext.FlushAsync(cancellationToken).ConfigureAwait(false); + FlushResult result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + if (result.IsCanceled || result.IsCompleted) + { + if (result.IsCanceled) + { + ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled(); + } + } } finally { - serializationContext.Dispose(); + if (pipeWriter is PooledByteBufferWriter disposable) + { + disposable.Dispose(); + } } } else if ( @@ -127,7 +143,7 @@ rootValue is not null && Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? derivedTypeInfo)) { Debug.Assert(typeof(T) == typeof(object)); - await derivedTypeInfo.SerializeAsObjectAsync(serializationContext, rootValue, cancellationToken).ConfigureAwait(false); + await derivedTypeInfo.SerializeAsObjectAsync(pipeWriter, rootValue, flushThreshold, cancellationToken).ConfigureAwait(false); } else { @@ -138,16 +154,21 @@ rootValue is not null && supportContinuation: true, supportAsync: true); + if (!pipeWriter.CanGetUnflushedBytes) + { + ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(pipeWriter); + } + state.PipeWriter = pipeWriter; state.CancellationToken = cancellationToken; - var writer = new Utf8JsonWriter(serializationContext.BufferWriter, Options.GetWriterOptions()); + var writer = new Utf8JsonWriter(pipeWriter, Options.GetWriterOptions()); try { + state.FlushThreshold = flushThreshold; + do { - state.FlushThreshold = serializationContext.FlushThreshold; - try { isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state); @@ -161,7 +182,17 @@ rootValue is not null && } else { - await serializationContext.FlushAsync(cancellationToken).ConfigureAwait(false); + FlushResult result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + if (result.IsCanceled || result.IsCompleted) + { + if (result.IsCanceled) + { + ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled(); + } + + // Pipe is completed, no one is reading so no point in continuing serialization + return; + } } } finally @@ -209,7 +240,10 @@ rootValue is not null && finally { writer.Dispose(); - serializationContext.Dispose(); + if (pipeWriter is PooledByteBufferWriter disposable) + { + disposable.Dispose(); + } } } } @@ -270,10 +304,15 @@ rootValue is not null && using var bufferWriter = new PooledByteBufferWriter(Options.DefaultBufferSize); using var writer = new Utf8JsonWriter(bufferWriter, Options.GetWriterOptions()); - do + if (!bufferWriter.CanGetUnflushedBytes) { - state.FlushThreshold = (int)(bufferWriter.Capacity * JsonSerializer.FlushThreshold); + ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(bufferWriter); + } + state.PipeWriter = bufferWriter; + state.FlushThreshold = (int)(bufferWriter.Capacity * JsonSerializer.FlushThreshold); + do + { isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state); writer.Flush(); @@ -297,8 +336,8 @@ rootValue is not null && internal sealed override void SerializeAsObject(Utf8JsonWriter writer, object? rootValue) => Serialize(writer, JsonSerializer.UnboxOnWrite(rootValue), rootValue); - internal sealed override Task SerializeAsObjectAsync(TSerializationContext serializationContext, object? rootValue, CancellationToken cancellationToken) - => SerializeAsync(serializationContext, JsonSerializer.UnboxOnWrite(rootValue), cancellationToken, rootValue); + internal sealed override Task SerializeAsObjectAsync(PipeWriter pipeWriter, object? rootValue, int flushThreshold, CancellationToken cancellationToken) + => SerializeAsync(pipeWriter, JsonSerializer.UnboxOnWrite(rootValue), flushThreshold, cancellationToken, rootValue); internal sealed override Task SerializeAsObjectAsync(Stream utf8Json, object? rootValue, CancellationToken cancellationToken) => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite(rootValue), cancellationToken, rootValue); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs index e52998775a7b5..7a00b678d5989 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs @@ -97,6 +97,22 @@ private protected override void SetCreateObject(Delegate? createObject) _createObject = untypedCreateObject; _typedCreateObject = typedCreateObject; + + // Clear any data related to the previously specified ctor + ConstructorAttributeProviderFactory = null; + ConstructorAttributeProvider = null; + + if (CreateObjectWithArgs is not null) + { + _parameterInfoValuesIndex = null; + CreateObjectWithArgs = null; + ParameterCount = 0; + + foreach (JsonPropertyInfo propertyInfo in PropertyList) + { + propertyInfo.AssociatedParameter = null; + } + } } /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index 32ab005991b5d..38517ffab7f01 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.IO.Pipelines; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Text.Json.Serialization; @@ -88,6 +89,8 @@ public readonly ref WriteStackFrame Parent /// public int FlushThreshold; + public PipeWriter? PipeWriter; + /// /// Indicates that the state still contains suspended frames waiting re-entry. /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index b30a58b717e66..110dcba34fa2c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.IO.Pipelines; using System.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -929,9 +930,9 @@ public static void ThrowOperationCanceledException_PipeWriteCanceled() } [DoesNotReturn] - public static void ThrowOperationCanceledException_PipeWriteCompleted() + public static void ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(PipeWriter pipeWriter) { - throw new OperationCanceledException(SR.PipeWriterCompleted); + throw new InvalidOperationException(SR.Format(SR.PipeWriter_DoesNotImplementUnflushedBytes, pipeWriter.GetType().Name)); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.JsonSerializer.cs similarity index 99% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs rename to src/libraries/System.Text.Json/tests/Common/MetadataTests.JsonSerializer.cs index d0c1cdeb21b5c..217241f21b565 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.JsonSerializer.cs +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.JsonSerializer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs similarity index 99% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs rename to src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs index 924d518815099..8341415bd8481 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.Options.cs +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; -using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using Xunit; diff --git a/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs new file mode 100644 index 0000000000000..f25c5fe80a5be --- /dev/null +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs @@ -0,0 +1,530 @@ +// 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.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Text.Json.Serialization.Metadata; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public partial class MetadataTests(JsonSerializerWrapper serializerUnderTest) : SerializerTests(serializerUnderTest) + { + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(int?))] + [InlineData(typeof(string))] + [InlineData(typeof(List))] + [InlineData(typeof(Dictionary))] + [InlineData(typeof(ClassWithoutCtor))] + [InlineData(typeof(StructWithDefaultCtor?))] + [InlineData(typeof(IInterfaceWithProperties))] + [InlineData(typeof(IDerivedInterface))] + public void TypeWithoutConstructor_TypeInfoReportsNullCtorProvider(Type type) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type); + + Assert.Null(typeInfo.ConstructorAttributeProvider); + } + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + public void TypeWithConstructor_TypeInfoReportsExpectedCtorProvider(Type typeWithCtor) + { + ConstructorInfo? expectedCtor = typeWithCtor.GetConstructors(BindingFlags.Public | BindingFlags.Instance) + .OrderByDescending(ctor => ctor.GetCustomAttribute() is not null) + .FirstOrDefault(); + + Assert.NotNull(expectedCtor); + + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor); + + Assert.Same(expectedCtor, typeInfo.ConstructorAttributeProvider); + } + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + public void TypeWithConstructor_SettingCtorDelegate_ResetsCtorAttributeProvider(Type typeWithCtor) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor, mutable: true); + Assert.NotNull(typeInfo.ConstructorAttributeProvider); + + typeInfo.CreateObject = () => Activator.CreateInstance(typeWithCtor); + + Assert.Null(typeInfo.ConstructorAttributeProvider); + } + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(StructWithParameterizedCtor))] + [InlineData(typeof(ClassWithoutCtor))] + [InlineData(typeof(IInterfaceWithProperties))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + [InlineData(typeof(IDerivedInterface))] + public void JsonPropertyInfo_DeclaringType_HasExpectedValue(Type typeWithProperties) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithProperties); + Assert.NotEmpty(typeInfo.Properties); + + Assert.All(typeInfo.Properties, propertyInfo => + { + Assert.True(propertyInfo.DeclaringType.IsAssignableFrom(typeInfo.Type)); + }); + } + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(ClassWithoutCtor))] + [InlineData(typeof(StructWithParameterizedCtor))] + [InlineData(typeof(IInterfaceWithProperties))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + [InlineData(typeof(IDerivedInterface))] + public void JsonPropertyInfo_AttributeProvider_HasExpectedValue(Type typeWithProperties) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithProperties); + Assert.NotEmpty(typeInfo.Properties); + + Assert.All(typeInfo.Properties, propertyInfo => + { + MemberInfo? memberInfo = ResolveMember(typeWithProperties, propertyInfo.Name); + Assert.Same(memberInfo, propertyInfo.AttributeProvider); + }); + } + + [Theory] + [InlineData(typeof(ClassWithoutCtor))] + [InlineData(typeof(IInterfaceWithProperties))] + [InlineData(typeof(IDerivedInterface))] + public void TypeWithoutConstructor_JsonPropertyInfo_AssociatedParameter_IsNull(Type type) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type); + Assert.NotEmpty(typeInfo.Properties); + + Assert.All(typeInfo.Properties, propertyInfo => Assert.Null(propertyInfo.AssociatedParameter)); + } + + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(StructWithParameterizedCtor))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + public void TypeWithConstructor_JsonPropertyInfo_AssociatedParameter_MatchesCtorParams(Type typeWithCtor) + { + ConstructorInfo? expectedCtor = typeWithCtor.GetConstructors(BindingFlags.Public | BindingFlags.Instance) + .OrderByDescending(ctor => ctor.GetCustomAttribute() is not null) + .FirstOrDefault(); + + Assert.NotNull(expectedCtor); + + Dictionary parameters = expectedCtor.GetParameters().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase); + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor); + + Assert.All(typeInfo.Properties, jsonProperty => + { + JsonParameterInfo? jsonParameter = jsonProperty.AssociatedParameter; + + if (!parameters.TryGetValue(jsonProperty.Name, out ParameterInfo? parameter)) + { + Assert.Null(jsonParameter); + return; + } + + Assert.NotNull(jsonParameter); + + Assert.Equal(typeWithCtor, jsonParameter.DeclaringType); + Assert.Equal(parameter.Position, jsonParameter.Position); + Assert.Equal(parameter.ParameterType, jsonParameter.ParameterType); + Assert.Equal(parameter.Name, jsonParameter.Name); + Assert.Equal(parameter.Name, jsonProperty.Name, StringComparer.OrdinalIgnoreCase); + + Assert.Equal(parameter.HasDefaultValue, jsonParameter.HasDefaultValue); + Assert.Equal(GetDefaultValue(parameter), jsonParameter.DefaultValue); + Assert.Same(parameter, jsonParameter.AttributeProvider); + Assert.Equal(jsonProperty.IsSetNullable, jsonParameter.IsNullable); + Assert.False(jsonParameter.IsMemberInitializer); + + parameters.Remove(jsonProperty.Name); + }); + + Assert.Empty(parameters); + } + + [Theory] + [InlineData(typeof(ClassWithRequiredMember))] + [InlineData(typeof(ClassWithInitOnlyProperty))] + public void TypeWithRequiredOrInitMember_SourceGen_HasAssociatedParameterInfo(Type type) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type); + JsonPropertyInfo propertyInfo = typeInfo.Properties.Single(); + + JsonParameterInfo? jsonParameter = propertyInfo.AssociatedParameter; + + if (Serializer.IsSourceGeneratedSerializer) + { + Assert.NotNull(jsonParameter); + + Assert.Equal(type, jsonParameter.DeclaringType); + Assert.Equal(0, jsonParameter.Position); + Assert.Equal(propertyInfo.PropertyType, jsonParameter.ParameterType); + Assert.Equal(propertyInfo.Name, jsonParameter.Name); + + Assert.False(jsonParameter.HasDefaultValue); + Assert.Null(jsonParameter.DefaultValue); + Assert.Null(jsonParameter.AttributeProvider); + Assert.Equal(propertyInfo.IsSetNullable, jsonParameter.IsNullable); + Assert.True(jsonParameter.IsMemberInitializer); + } + else + { + Assert.Null(jsonParameter); + } + } + + [Theory] + [InlineData(typeof(ClassWithDefaultCtor))] + [InlineData(typeof(StructWithDefaultCtor))] + [InlineData(typeof(ClassWithParameterizedCtor))] + [InlineData(typeof(StructWithParameterizedCtor))] + [InlineData(typeof(ClassWithRequiredMember))] + [InlineData(typeof(ClassWithInitOnlyProperty))] + [InlineData(typeof(ClassWithMultipleConstructors))] + [InlineData(typeof(DerivedClassWithShadowingProperties))] + public void TypeWithConstructor_SettingCtorDelegate_ResetsAssociatedParameters(Type typeWithCtor) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor, mutable: true); + Assert.NotEmpty(typeInfo.Properties); + + typeInfo.CreateObject = () => Activator.CreateInstance(typeWithCtor); + + Assert.All(typeInfo.Properties, propertyInfo => Assert.Null(propertyInfo.AssociatedParameter)); + } + + [Theory] + [InlineData(typeof(int), null)] + [InlineData(typeof(int?), null)] + [InlineData(typeof(string), null)] + [InlineData(typeof(ClassWithDefaultCtor), null)] + [InlineData(typeof(StructWithDefaultCtor), null)] + [InlineData(typeof(StructWithDefaultCtor?), null)] + [InlineData(typeof(string[]), null)] + [InlineData(typeof(List), null)] + [InlineData(typeof(IList), null)] + [InlineData(typeof(ImmutableArray), null)] + [InlineData(typeof(DerivedList), null)] + [InlineData(typeof(DerivedListWithCustomConverter), null)] + [InlineData(typeof(Dictionary), typeof(Guid))] + [InlineData(typeof(IReadOnlyDictionary), typeof(Guid))] + [InlineData(typeof(ImmutableDictionary), typeof(Guid))] + [InlineData(typeof(DerivedDictionary), typeof(Guid))] + [InlineData(typeof(DerivedDictionaryWithCustomConverter), null)] + [InlineData(typeof(ArrayList), null)] + [InlineData(typeof(Hashtable), typeof(string))] + public void JsonTypeInfo_KeyType_ReturnsExpectedValue(Type type, Type? expectedKeyType) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type); + Assert.Equal(expectedKeyType, typeInfo.KeyType); + } + + [Theory] + [InlineData(typeof(int), null)] + [InlineData(typeof(int?), typeof(int))] + [InlineData(typeof(string), null)] + [InlineData(typeof(ClassWithDefaultCtor), null)] + [InlineData(typeof(StructWithDefaultCtor), null)] + [InlineData(typeof(StructWithDefaultCtor?), typeof(StructWithDefaultCtor))] + [InlineData(typeof(string[]), typeof(string))] + [InlineData(typeof(List), typeof(string))] + [InlineData(typeof(IList), typeof(string))] + [InlineData(typeof(ImmutableArray), typeof(string))] + [InlineData(typeof(DerivedList), typeof((int, Guid)))] + [InlineData(typeof(DerivedListWithCustomConverter), null)] + [InlineData(typeof(Dictionary), typeof(int))] + [InlineData(typeof(IReadOnlyDictionary), typeof(int))] + [InlineData(typeof(ImmutableDictionary), typeof(int))] + [InlineData(typeof(DerivedDictionary), typeof(int))] + [InlineData(typeof(DerivedDictionaryWithCustomConverter), null)] + [InlineData(typeof(ArrayList), typeof(object))] + [InlineData(typeof(Hashtable), typeof(object))] + public void JsonTypeInfo_ElementType_ReturnsExpectedValue(Type type, Type? expectedKeyType) + { + JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type); + Assert.Equal(expectedKeyType, typeInfo.ElementType); + } + + private static object? GetDefaultValue(ParameterInfo parameterInfo) + { + Type parameterType = parameterInfo.ParameterType; + object? defaultValue = parameterInfo.DefaultValue; + + if (defaultValue is null) + { + return null; + } + + // DBNull.Value is sometimes used as the default value (returned by reflection) of nullable params in place of null. + if (defaultValue == DBNull.Value && parameterType != typeof(DBNull)) + { + return null; + } + + // Default values of enums or nullable enums are represented using the underlying type and need to be cast explicitly + // cf. https://github.com/dotnet/runtime/issues/68647 + if (parameterType.IsEnum) + { + return Enum.ToObject(parameterType, defaultValue); + } + + if (Nullable.GetUnderlyingType(parameterType) is Type underlyingType && underlyingType.IsEnum) + { + return Enum.ToObject(underlyingType, defaultValue); + } + + return defaultValue; + } + + private static MemberInfo? ResolveMember(Type type, string name) + { + MemberInfo? result = type.GetMember(name, BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(); + if (result is null && type.IsInterface) + { + return type.GetInterfaces().Select(i => ResolveMember(i, name)).FirstOrDefault(m => m is not null); + } + + return result; + } + + internal class ClassWithoutCtor + { + private ClassWithoutCtor() { } + + public int Value { get; set; } + public string Value2 { get; set; } + + [JsonInclude] + public bool Value3 = true; + } + + internal interface IInterfaceWithProperties + { + public int Value { get; set; } + public string Value2 { get; set; } + } + + internal struct StructWithDefaultCtor + { + [JsonConstructor] + public StructWithDefaultCtor() + { + } + + public int Value { get; set; } + public string Value2 { get; set; } + + [JsonInclude] + public bool Value3 = true; + } + + internal class ClassWithDefaultCtor + { + [JsonConstructor] + public ClassWithDefaultCtor() + { + } + + public int Value { get; set; } + public string Value2 { get; set; } + + [JsonInclude] + public bool Value3 = true; + } + + internal class ClassWithParameterizedCtor + { + [JsonConstructor] + public ClassWithParameterizedCtor( + string x1, int x2, bool x3, BindingFlags x4, + string x5 = "str", int x6 = 42, bool x7 = true, BindingFlags? x8 = BindingFlags.Instance) + { + X1 = x1; + X2 = x2; + X3 = x3; + X4 = x4; + X5 = x5; + X6 = x6; + X7 = x7; + X8 = x8; + } + + public string X1 { get; } + public int X2 { get; } + public bool X3 { get; } + public BindingFlags X4 { get; } + public string X5 { get; } + public int X6 { get; } + public bool X7 { get; } + + [JsonInclude] + public BindingFlags? X8; + + public string ExtraProperty { get; set; } + } + + internal struct StructWithParameterizedCtor + { + [JsonConstructor] + public StructWithParameterizedCtor( + string x1, int x2, bool x3, BindingFlags x4, + string x5 = "str", int x6 = 42, bool x7 = true, BindingFlags? x8 = BindingFlags.Instance) + { + X1 = x1; + X2 = x2; + X3 = x3; + X4 = x4; + X5 = x5; + X6 = x6; + X7 = x7; + X8 = x8; + } + + public string X1 { get; } + public int X2 { get; } + public bool X3 { get; } + public BindingFlags X4 { get; } + public string X5 { get; } + public int X6 { get; } + public bool X7 { get; } + + [JsonInclude] + public BindingFlags? X8; + + public string ExtraProperty { get; set; } + } + + internal class ClassWithRequiredMember + { + public required int Value { get; set; } + } + + internal class ClassWithInitOnlyProperty + { + public int Value { get; init; } + } + + internal class ClassWithMultipleConstructors + { + public ClassWithMultipleConstructors() { } + + public ClassWithMultipleConstructors(int x) { } + + [JsonConstructor] + public ClassWithMultipleConstructors(string value) + { + Value = value; + } + + public string Value { get; set; } + } + + internal abstract class BaseClassWithProperties + { + public int Value1 { get; } + public virtual int Value2 { get; set; } + public abstract int Value3 { get; set; } + } + + internal class DerivedClassWithShadowingProperties : BaseClassWithProperties + { + [JsonConstructor] + public DerivedClassWithShadowingProperties(string value1, int value2, int value3) + { + Value1 = value1; + Value2 = value2; + Value3 = value3; + } + + public new string Value1 { get; set; } + public override int Value2 { get; set; } + public override int Value3 { get; set; } + } + + internal interface IBaseInterface1 + { + int Value1 { get; } + } + + internal interface IBaseInterface2 + { + int Value2 { get; set; } + } + + internal interface IDerivedInterface : IBaseInterface1, IBaseInterface2 + { + new string Value2 { get; set; } + int Value3 { get; set; } + } + + internal class DerivedList : List<(T, Guid)>; + + [JsonConverter(typeof(CustomConverter))] + internal class DerivedListWithCustomConverter : List + { + public sealed class CustomConverter : JsonConverter + { + public override DerivedListWithCustomConverter? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, DerivedListWithCustomConverter value, JsonSerializerOptions options) => throw new NotImplementedException(); + } + } + + internal class DerivedDictionary : Dictionary; + + [JsonConverter(typeof(CustomConverter))] + internal class DerivedDictionaryWithCustomConverter : Dictionary + { + public sealed class CustomConverter : JsonConverter + { + public override DerivedDictionaryWithCustomConverter? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, DerivedDictionaryWithCustomConverter value, JsonSerializerOptions options) => throw new NotImplementedException(); + } + } + } + + internal class WeatherForecastWithPOCOs + { + public DateTimeOffset Date { get; set; } + public int TemperatureCelsius { get; set; } + public string? Summary { get; set; } + public string? SummaryField; + public List? DatesAvailable { get; set; } + public Dictionary? TemperatureRanges { get; set; } + public string[]? SummaryWords { get; set; } + } + + public class HighLowTemps + { + public int High { get; set; } + public int Low { get; set; } + } + + [JsonSerializable(typeof(WeatherForecastWithPOCOs))] + internal sealed partial class JsonContext : JsonSerializerContext; +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs new file mode 100644 index 0000000000000..2dee7e6da78f4 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs @@ -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. + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Tests; + +namespace System.Text.Json.SourceGeneration.Tests +{ + public partial class MetadataTests_SourceGen() : MetadataTests(new StringSerializerWrapper(Context.Default)) + { + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(int?))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(string[]))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(IList))] + [JsonSerializable(typeof(ImmutableArray))] + [JsonSerializable(typeof(DerivedList))] + [JsonSerializable(typeof(DerivedListWithCustomConverter))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(ImmutableDictionary))] + [JsonSerializable(typeof(DerivedDictionary))] + [JsonSerializable(typeof(DerivedDictionaryWithCustomConverter))] + [JsonSerializable(typeof(ArrayList))] + [JsonSerializable(typeof(Hashtable))] + [JsonSerializable(typeof(ClassWithoutCtor))] + [JsonSerializable(typeof(IInterfaceWithProperties))] + [JsonSerializable(typeof(ClassWithDefaultCtor))] + [JsonSerializable(typeof(StructWithDefaultCtor))] + [JsonSerializable(typeof(StructWithDefaultCtor?))] + [JsonSerializable(typeof(ClassWithParameterizedCtor))] + [JsonSerializable(typeof(StructWithParameterizedCtor))] + [JsonSerializable(typeof(ClassWithRequiredMember))] + [JsonSerializable(typeof(ClassWithInitOnlyProperty))] + [JsonSerializable(typeof(ClassWithMultipleConstructors))] + [JsonSerializable(typeof(DerivedClassWithShadowingProperties))] + [JsonSerializable(typeof(IDerivedInterface))] + partial class Context : JsonSerializerContext; + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index fdbc936afc1ac..7de497308e6ba 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -83,6 +83,9 @@ + + + @@ -121,6 +124,7 @@ + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs index eacb292bfc20f..631be65d8f24f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs @@ -695,6 +695,7 @@ public static void CreateJsonPropertyInfo_ReturnsCorrectTypeOnPolymorphicConvert // The generic parameter of the property metadata type // should match that of property type and not the converter type. + Assert.Equal(typeof(MyClass), jpi.DeclaringType); Assert.Equal(typeof(string), jpi.GetType().GetGenericArguments()[0]); Assert.Equal(typeof(string), jpi.PropertyType); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs index b1339b85cb746..a400e302ad635 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs @@ -1,86 +1,8 @@ // 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.Text.Json.Serialization; -using System.Text.Json.Serialization.Tests; - namespace System.Text.Json.Serialization.Tests { - public sealed class MetadataTests_Span : MetadataTests - { - public MetadataTests_Span() : base(JsonSerializerWrapper.SpanSerializer) { } - } - - public sealed class MetadataTests_String : MetadataTests - { - public MetadataTests_String() : base(JsonSerializerWrapper.StringSerializer) { } - } - - public sealed class MetadataTests_AsyncStream : MetadataTests - { - public MetadataTests_AsyncStream() : base(JsonSerializerWrapper.AsyncStreamSerializer) { } - } - - public sealed class MetadataTests_SyncStream : MetadataTests - { - public MetadataTests_SyncStream() : base(JsonSerializerWrapper.SyncStreamSerializer) { } - } - - public sealed class MetadataTests_LowLevel : MetadataTests - { - public MetadataTests_LowLevel() : base(JsonSerializerWrapper.ReaderWriterSerializer) { } - } - - public class MetadataTests_Document : MetadataTests - { - public MetadataTests_Document() : base(JsonSerializerWrapper.DocumentSerializer) { } - } - - public class MetadataTests_Element : MetadataTests - { - public MetadataTests_Element() : base(JsonSerializerWrapper.ElementSerializer) { } - } - - public class MetadataTests_Node : MetadataTests - { - public MetadataTests_Node() : base(JsonSerializerWrapper.NodeSerializer) { } - } - - public class MetadataTests_Pipe : MetadataTests - { - public MetadataTests_Pipe() : base(JsonSerializerWrapper.AsyncPipeSerializer) { } - } - - public abstract partial class MetadataTests - { - protected JsonSerializerWrapper Serializer { get; } - - public MetadataTests(JsonSerializerWrapper serializer) - { - Serializer = serializer; - } - } - - internal class WeatherForecastWithPOCOs - { - public DateTimeOffset Date { get; set; } - public int TemperatureCelsius { get; set; } - public string? Summary { get; set; } - public string? SummaryField; - public List? DatesAvailable { get; set; } - public Dictionary? TemperatureRanges { get; set; } - public string[]? SummaryWords { get; set; } - } - - public class HighLowTemps - { - public int High { get; set; } - public int Low { get; set; } - } - - [JsonSerializable(typeof(WeatherForecastWithPOCOs))] - internal sealed partial class JsonContext : JsonSerializerContext - { - } + public sealed class MetadataTests_String() : MetadataTests(JsonSerializerWrapper.StringSerializer); + public sealed class MetadataTests_AsyncStream() : MetadataTests(JsonSerializerWrapper.AsyncStreamSerializer); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index 3ada5243181d7..f5ef9964d8f1c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -960,6 +960,60 @@ public static void Options_RespectNullableAnnotationsDefault_FeatureSwitch(bool? }, arg, options).Dispose(); } + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void Options_NullabilityInfoFeatureSwitchDisabled_ReportsPropertiesAsNullable() + { + var options = new RemoteInvokeOptions() + { + RuntimeConfigurationOptions = + { + ["System.Reflection.NullabilityInfoContext.IsSupported"] = false + } + }; + + RemoteExecutor.Invoke(static () => + { + var value = new NullableAnnotationsTests.NotNullablePropertyClass(); + string expectedJson = """{"Property":null}"""; + + Assert.Null(value.Property); + string json = JsonSerializer.Serialize(value); + Assert.Equal(expectedJson, json); + value = JsonSerializer.Deserialize(json); + Assert.Null(value.Property); + + }, options).Dispose(); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void Options_NullabilityInfoFeatureSwitchDisabled_RespectNullabilityAnnotationsEnabled_ThrowsInvalidOperationException() + { + var options = new RemoteInvokeOptions() + { + RuntimeConfigurationOptions = + { + ["System.Reflection.NullabilityInfoContext.IsSupported"] = false + } + }; + + RemoteExecutor.Invoke(static () => + { + var jsonOptions = new JsonSerializerOptions { RespectNullableAnnotations = true }; + var value = new NullableAnnotationsTests.NotNullablePropertyClass(); + string expectedJson = """{"Property":null}"""; + InvalidOperationException ex; + + ex = Assert.Throws(() => JsonSerializer.Serialize(value, jsonOptions)); + Assert.Contains("System.Reflection.NullabilityInfoContext.IsSupported", ex.Message); + + ex = Assert.Throws(() => JsonSerializer.Deserialize(expectedJson, jsonOptions)); + Assert.Contains("System.Reflection.NullabilityInfoContext.IsSupported", ex.Message); + + }, options).Dispose(); + } + private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue) { var options = new JsonSerializerOptions(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Pipe.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Pipe.WriteTests.cs index 7e2a9502df973..80be8022c5c5c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Pipe.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Pipe.WriteTests.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO.Pipelines; +using System.Linq; using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; @@ -44,12 +45,45 @@ public async Task CompletedPipeWithExceptionThrowsFromSerialize() } [Fact] - public async Task CompletedPipeThrowsFromSerialize() + public async Task FlushesPeriodicallyWhenWritingLargeJson() { - Pipe pipe = new Pipe(); - pipe.Reader.Complete(); + Pipe pipe = new Pipe(new PipeOptions(pauseWriterThreshold: 1000000)); + IEnumerable obj = Enumerable.Range(0, PipeOptions.Default.MinimumSegmentSize * 2); + CustomPipeWriter writer = new CustomPipeWriter(pipe.Writer); + await JsonSerializer.SerializeAsync(writer, obj); - await Assert.ThrowsAsync(() => JsonSerializer.SerializeAsync(pipe.Writer, 1)); + Assert.Equal(3, writer.Flushes.Count); + foreach (long flush in writer.Flushes) + { + // Fragile check, but since we're writing integers that are 5 or less digits + // it should always exit to a flush while under the flush threshold + Assert.True(flush < PipeOptions.Default.MinimumSegmentSize * 4); + } + } + + class CustomPipeWriter : PipeWriter + { + private readonly PipeWriter _originalWriter; + + public CustomPipeWriter(PipeWriter originalWriter) + { + _originalWriter = originalWriter; + } + + public List Flushes { get; } = new List(); + + public override void Advance(int bytes) => _originalWriter.Advance(bytes); + public override void CancelPendingFlush() => _originalWriter.CancelPendingFlush(); + public override void Complete(Exception? exception = null) => _originalWriter.Complete(exception); + public override ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + Flushes.Add(UnflushedBytes); + return _originalWriter.FlushAsync(cancellationToken); + } + public override Memory GetMemory(int sizeHint = 0) => _originalWriter.GetMemory(sizeHint); + public override Span GetSpan(int sizeHint = 0) => _originalWriter.GetSpan(sizeHint); + public override bool CanGetUnflushedBytes => _originalWriter.CanGetUnflushedBytes; + public override long UnflushedBytes => _originalWriter.UnflushedBytes; } [Fact] @@ -69,7 +103,7 @@ public async Task CancelPendingFlushDuringBackpressureThrows() // Technically this check is not needed, but helps confirm behavior, that Pipe had written but was waiting for flush to continue. // result.Buffer: 123456789[0... - Assert.Equal(10 + i - 1, result.Buffer.Length); + Assert.InRange(result.Buffer.Length, 10 + i - 1, 10 + i + 1); pipe.Reader.AdvanceTo(result.Buffer.End); async IAsyncEnumerable GetNumbersAsync() @@ -298,7 +332,102 @@ public async Task BuffersBehaveAsExpected() Assert.Equal(0, pool.BufferCount); result = await pipe.Reader.ReadAsync(); Assert.Equal(2002, result.Buffer.Length); + } + + [Fact] + public async Task ThrowForPipeWriterWithoutUnflushedBytesImplemented() + { + var pipeWriter = new BadPipeWriter(); + var exception = await Assert.ThrowsAnyAsync(() => JsonSerializer.SerializeAsync(pipeWriter, 0)); + Assert.Equal("The PipeWriter 'BadPipeWriter' does not implement PipeWriter.UnflushedBytes.", exception.Message); + } + + class BadPipeWriter : PipeWriter + { + public override void Advance(int bytes) => throw new NotImplementedException(); + public override void CancelPendingFlush() => throw new NotImplementedException(); + public override void Complete(Exception? exception = null) => throw new NotImplementedException(); + public override ValueTask FlushAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public override Memory GetMemory(int sizeHint = 0) => throw new NotImplementedException(); + public override Span GetSpan(int sizeHint = 0) => throw new NotImplementedException(); + public override bool CanGetUnflushedBytes => false; + public override long UnflushedBytes => throw new NotImplementedException(); + } + [Fact] + public async Task NestedSerializeAsyncCallsFlushAtThreshold() + { + string data = new string('a', 300); + var options = new JsonSerializerOptions(); + options.Converters.Add(new MyStringConverter()); + + var pipe = new Pipe(new PipeOptions(pauseWriterThreshold: 10000000)); + var writer = new CustomPipeWriter(pipe.Writer); + await JsonSerializer.SerializeAsync(writer, CreateManyTestObjects(), options); + + // Flush should happen every ~14,745 bytes (+36 for writing data when just below threshold) + Assert.True(writer.Flushes.Count > (data.Length * 10_000 / 16_000), $"Flush count: {writer.Flushes.Count}"); + + foreach (long flush in writer.Flushes) + { + Assert.True(flush < PipeOptions.Default.MinimumSegmentSize * 4); + } + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (true) + { + if (++i % 10_000 == 0) + { + break; + } + yield return data; + } + } + } + + [Fact] + public async Task SerializeAsyncCallsFlushAtThreshold() + { + string data = new string('a', 300); + var options = new JsonSerializerOptions(); + + var pipe = new Pipe(new PipeOptions(pauseWriterThreshold: 10000000)); + var writer = new CustomPipeWriter(pipe.Writer); + await JsonSerializer.SerializeAsync(writer, CreateManyTestObjects(), options); + + // Flush should happen every ~14,745 bytes (+36 for writing data when just below threshold) + Assert.True(writer.Flushes.Count > (data.Length * 10_000 / 16_000), $"Flush count: {writer.Flushes.Count}"); + + foreach (long flush in writer.Flushes) + { + Assert.True(flush < PipeOptions.Default.MinimumSegmentSize * 4); + } + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (true) + { + if (++i % 10_000 == 0) + { + break; + } + yield return data; + } + } + } + + class MyStringConverter : JsonConverter + { + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + throw new NotImplementedException(); + + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value); + } } internal class TestPool : MemoryPool diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs index c3734c58f6927..22572f9a8fd98 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.WriteTests.cs @@ -3,9 +3,10 @@ using System.Collections.Generic; using System.IO; +using System.Text.Json.Serialization.Metadata; using System.Text.Json.Serialization.Tests.Schemas.OrderPayload; +using System.Threading; using System.Threading.Tasks; -using System.Text.Json.Serialization.Metadata; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -128,7 +129,14 @@ private async Task WriteAsync(TestStream stream) Assert.Equal(551_368, stream.TestWriteBytesCount); // We should have more than one write called due to the large byte count. - Assert.InRange(stream.TestWriteCount, 1, int.MaxValue); + if (Serializer.IsAsyncSerializer) + { + Assert.InRange(stream.TestAsyncWriteCount, 1, int.MaxValue); + } + else + { + Assert.InRange(stream.TestWriteCount, 1, int.MaxValue); + } // We don't auto-flush. Assert.Equal(0, stream.TestFlushCount); @@ -408,6 +416,115 @@ public async Task NestedJsonFileCircularDependencyTest(int depthFactor) } } + [Fact] + public void NestedSerializeCallsFlushAtThreshold() + { + var data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + var options = new JsonSerializerOptions { DefaultBufferSize = 512 }; + options.Converters.Add(new MyStringConverter()); + + using var stream = new TestStream(1); + JsonSerializer.Serialize(stream, CreateManyTestObjects(), options); + + // Flush should happen every ~460 bytes (+36 for writing data when just below threshold) + // Assuming a "perfect" array pool implementation, the number of write calls should be closer to (data.Length * 10_000 / (512 * .9)) + // But because the array pool may give a larger buffer than 512, we need to be a little more permissive when checking how many writes occur + Assert.InRange(stream.TestWriteCount, data.Length * 10_000 / 5000, data.Length * 10_000 / 200); + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (++i < 10_000) + { + yield return data; + } + } + } + + [Fact] + public void SerializeCallsFlushAtThreshold() + { + var data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + var options = new JsonSerializerOptions { DefaultBufferSize = 512 }; + + using var stream = new TestStream(1); + JsonSerializer.Serialize(stream, CreateManyTestObjects(), options); + + // Flush should happen every ~460 bytes (+36 for writing data when just below threshold) + // Assuming a "perfect" array pool implementation, the number of write calls should be closer to (data.Length * 10_000 / (512 * .9)) + // But because the array pool may give a larger buffer than 512, we need to be a little more permissive when checking how many writes occur + Assert.InRange(stream.TestWriteCount, data.Length * 10_000 / 5000, data.Length * 10_000 / 200); + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (++i < 10_000) + { + yield return data; + } + } + } + + [Fact] + public async Task NestedSerializeAsyncCallsFlushAtThreshold() + { + var data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + var options = new JsonSerializerOptions { DefaultBufferSize = 512 }; + options.Converters.Add(new MyStringConverter()); + + using var stream = new TestStream(1); + await JsonSerializer.SerializeAsync(stream, CreateManyTestObjects(), options); + + // Flush should happen every ~460 bytes (+36 for writing data when just below threshold) + // Assuming a "perfect" array pool implementation, the number of write calls should be closer to (data.Length * 10_000 / (512 * .9)) + // But because the array pool may give a larger buffer than 512, we need to be a little more permissive when checking how many writes occur + Assert.InRange(stream.TestAsyncWriteCount, data.Length * 10_000 / 5000, data.Length * 10_000 / 200); + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (++i < 10_000) + { + yield return data; + } + } + } + + [Fact] + public async Task SerializeAsyncCallsFlushAtThreshold() + { + var data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + var options = new JsonSerializerOptions { DefaultBufferSize = 512 }; + + using var stream = new TestStream(1); + await JsonSerializer.SerializeAsync(stream, CreateManyTestObjects(), options); + + // Flush should happen every ~460 bytes (+36 for writing data when just below threshold) + // Assuming a "perfect" array pool implementation, the number of write calls should be closer to (data.Length * 10_000 / (512 * .9)) + // But because the array pool may give a larger buffer than 512, we need to be a little more permissive when checking how many writes occur + Assert.InRange(stream.TestAsyncWriteCount, data.Length * 10_000 / 5000, data.Length * 10_000 / 200); + + IEnumerable CreateManyTestObjects() + { + int i = 0; + while (++i < 10_000) + { + yield return data; + } + } + } + + class MyStringConverter : JsonConverter + { + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + throw new NotImplementedException(); + + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value); + } + } + [Theory] [InlineData(32)] [InlineData(128)] @@ -602,6 +719,7 @@ public sealed class TestStream : Stream public int TestFlushCount { get; private set; } public int TestWriteCount { get; private set; } + public int TestAsyncWriteCount { get; private set; } public int TestWriteBytesCount { get; private set; } public int TestReadCount { get; private set; } public int TestRequestedReadBytesCount { get; private set; } @@ -628,6 +746,13 @@ public override int Read(byte[] buffer, int offset, int count) return _stream.Read(buffer, offset, count); } + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + TestAsyncWriteCount++; + TestWriteBytesCount += (count - offset); + return _stream.WriteAsync(buffer, offset, count, cancellationToken); + } + public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin); public override void SetLength(long value) => _stream.SetLength(value); public override bool CanRead => _stream.CanRead; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index bbddf00081650..72a7a32210a31 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -68,6 +68,9 @@ + + + @@ -195,8 +198,6 @@ - - diff --git a/src/libraries/System.Text.RegularExpressions/README.md b/src/libraries/System.Text.RegularExpressions/README.md index 15a343b568afd..49e6da19475ad 100644 --- a/src/libraries/System.Text.RegularExpressions/README.md +++ b/src/libraries/System.Text.RegularExpressions/README.md @@ -1,5 +1,5 @@ # System.Text.RegularExpressions -This assembly provides regular expression functionality. The main type contained in the assembly is [Regex](https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex) class, which represents an immutable regular expression object which can be used to test for matches on given input strings or spans. +This assembly provides regular expression functionality. The main type contained in the assembly is [Regex](https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regex) class, which represents an immutable regular expression object which can be used to test for matches on given input strings or spans. Documentation can be found at https://learn.microsoft.com/dotnet/api/system.text.regularexpressions. diff --git a/src/libraries/System.Text.RegularExpressions/gen/DiagnosticDescriptors.cs b/src/libraries/System.Text.RegularExpressions/gen/DiagnosticDescriptors.cs index fa5330ea96953..3860261b5b2a6 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/DiagnosticDescriptors.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/DiagnosticDescriptors.cs @@ -37,10 +37,10 @@ internal static class DiagnosticDescriptors isEnabledByDefault: true, customTags: WellKnownDiagnosticTags.NotConfigurable); - public static DiagnosticDescriptor RegexMethodMustHaveValidSignature { get; } = DiagnosticDescriptorHelper.Create( + public static DiagnosticDescriptor RegexMemberMustHaveValidSignature { get; } = DiagnosticDescriptorHelper.Create( id: "SYSLIB1043", title: new LocalizableResourceString(nameof(SR.InvalidGeneratedRegexAttributeTitle), SR.ResourceManager, typeof(FxResources.System.Text.RegularExpressions.Generator.SR)), - messageFormat: new LocalizableResourceString(nameof(SR.RegexMethodMustHaveValidSignatureMessage), SR.ResourceManager, typeof(FxResources.System.Text.RegularExpressions.Generator.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.RegexMemberMustHaveValidSignatureMessage), SR.ResourceManager, typeof(FxResources.System.Text.RegularExpressions.Generator.SR)), category: Category, DiagnosticSeverity.Error, isEnabledByDefault: true, diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index b9a4b80a70bac..33755cfd62c8a 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -71,7 +71,12 @@ private static void EmitRegexPartialMethod(RegexMethod regexMethod, IndentedText writer.WriteLine($"/// "); writer.WriteLine($"/// "); writer.WriteLine($"[global::System.CodeDom.Compiler.{s_generatedCodeAttribute}]"); - writer.WriteLine($"{regexMethod.Modifiers} global::System.Text.RegularExpressions.Regex {regexMethod.MethodName}() => global::{GeneratedNamespace}.{regexMethod.GeneratedName}.Instance;"); + writer.Write($"{regexMethod.Modifiers} global::System.Text.RegularExpressions.Regex{(regexMethod.NullableRegex ? "?" : "")} {regexMethod.MemberName}"); + if (!regexMethod.IsProperty) + { + writer.Write("()"); + } + writer.WriteLine($" => global::{GeneratedNamespace}.{regexMethod.GeneratedName}.Instance;"); // Unwind all scopes while (writer.Indent != 0) @@ -89,7 +94,7 @@ private static void EmitRegexLimitedBoilerplate( if (langVer >= LanguageVersion.CSharp11) { visibility = "file"; - writer.WriteLine($"/// Caches a instance for the {rm.MethodName} method."); + writer.WriteLine($"/// Caches a instance for the {rm.MemberName} method."); } else { @@ -119,7 +124,7 @@ private static void EmitRegexLimitedBoilerplate( private static void EmitRegexDerivedImplementation( IndentedTextWriter writer, RegexMethod rm, string runnerFactoryImplementation, bool allowUnsafe) { - writer.WriteLine($"/// Custom -derived type for the {rm.MethodName} method."); + writer.WriteLine($"/// Custom -derived type for the {rm.MemberName} method."); writer.WriteLine($"[{s_generatedCodeAttribute}]"); if (allowUnsafe) { diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs index 8f8c6b38ad032..947d0bc0b049b 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Threading; @@ -25,7 +26,16 @@ public partial class RegexGenerator private static object? GetRegexMethodDataOrFailureDiagnostic( GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { - var methodSyntax = (MethodDeclarationSyntax)context.TargetNode; + if (context.TargetNode is IndexerDeclarationSyntax or AccessorDeclarationSyntax) + { + // We allow these to be used as a target node for the sole purpose + // of being able to flag invalid use when [GeneratedRegex] is applied incorrectly. + // Otherwise, if the ForAttributeWithMetadataName call excluded these, [GeneratedRegex] + // could be applied to them and we wouldn't be able to issue a diagnostic. + return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(context.TargetNode)); + } + + var memberSyntax = (MemberDeclarationSyntax)context.TargetNode; SemanticModel sm = context.SemanticModel; Compilation compilation = sm.Compilation; @@ -37,14 +47,14 @@ public partial class RegexGenerator return null; } - TypeDeclarationSyntax? typeDec = methodSyntax.Parent as TypeDeclarationSyntax; + TypeDeclarationSyntax? typeDec = memberSyntax.Parent as TypeDeclarationSyntax; if (typeDec is null) { return null; } - IMethodSymbol? regexMethodSymbol = context.TargetSymbol as IMethodSymbol; - if (regexMethodSymbol is null) + ISymbol? regexMemberSymbol = context.TargetSymbol is IMethodSymbol or IPropertySymbol ? context.TargetSymbol : null; + if (regexMemberSymbol is null) { return null; } @@ -52,19 +62,19 @@ public partial class RegexGenerator ImmutableArray boundAttributes = context.Attributes; if (boundAttributes.Length != 1) { - return new DiagnosticData(DiagnosticDescriptors.MultipleGeneratedRegexAttributes, GetComparableLocation(methodSyntax)); + return new DiagnosticData(DiagnosticDescriptors.MultipleGeneratedRegexAttributes, GetComparableLocation(memberSyntax)); } AttributeData generatedRegexAttr = boundAttributes[0]; if (generatedRegexAttr.ConstructorArguments.Any(ca => ca.Kind == TypedConstantKind.Error)) { - return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(methodSyntax)); + return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(memberSyntax)); } ImmutableArray items = generatedRegexAttr.ConstructorArguments; if (items.Length is 0 or > 4) { - return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(methodSyntax)); + return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(memberSyntax)); } string? pattern = items[0].Value as string; @@ -96,16 +106,36 @@ public partial class RegexGenerator if (pattern is null || cultureName is null) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), "(null)"); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "(null)"); } - if (!regexMethodSymbol.IsPartialDefinition || - regexMethodSymbol.IsAbstract || - regexMethodSymbol.Parameters.Length != 0 || - regexMethodSymbol.Arity != 0 || - !SymbolEqualityComparer.Default.Equals(regexMethodSymbol.ReturnType, regexSymbol)) + bool nullableRegex; + if (regexMemberSymbol is IMethodSymbol regexMethodSymbol) { - return new DiagnosticData(DiagnosticDescriptors.RegexMethodMustHaveValidSignature, GetComparableLocation(methodSyntax)); + if (!regexMethodSymbol.IsPartialDefinition || + regexMethodSymbol.IsAbstract || + regexMethodSymbol.Parameters.Length != 0 || + regexMethodSymbol.Arity != 0 || + !SymbolEqualityComparer.Default.Equals(regexMethodSymbol.ReturnType, regexSymbol)) + { + return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(memberSyntax)); + } + + nullableRegex = regexMethodSymbol.ReturnNullableAnnotation == NullableAnnotation.Annotated; + } + else + { + Debug.Assert(regexMemberSymbol is IPropertySymbol); + IPropertySymbol regexPropertySymbol = (IPropertySymbol)regexMemberSymbol; + if (!memberSyntax.Modifiers.Any(SyntaxKind.PartialKeyword) || // TODO: Switch to using regexPropertySymbol.IsPartialDefinition when available + regexPropertySymbol.IsAbstract || + regexPropertySymbol.SetMethod is not null || + !SymbolEqualityComparer.Default.Equals(regexPropertySymbol.Type, regexSymbol)) + { + return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(memberSyntax)); + } + + nullableRegex = regexPropertySymbol.NullableAnnotation == NullableAnnotation.Annotated; } RegexOptions regexOptions = options is not null ? (RegexOptions)options : RegexOptions.None; @@ -124,7 +154,7 @@ public partial class RegexGenerator } catch (Exception e) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), e.Message); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), e.Message); } if ((regexOptionsWithPatternOptions & RegexOptions.IgnoreCase) != 0 && !string.IsNullOrEmpty(cultureName)) @@ -132,7 +162,7 @@ public partial class RegexGenerator if ((regexOptions & RegexOptions.CultureInvariant) != 0) { // User passed in both a culture name and set RegexOptions.CultureInvariant which causes an explicit conflict. - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), "cultureName"); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "cultureName"); } try @@ -141,7 +171,7 @@ public partial class RegexGenerator } catch (CultureNotFoundException) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), "cultureName"); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "cultureName"); } } @@ -159,17 +189,17 @@ public partial class RegexGenerator RegexOptions.Singleline; if ((regexOptions & ~SupportedOptions) != 0) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), "options"); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "options"); } // Validate the timeout if (matchTimeout is 0 or < -1) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(methodSyntax), "matchTimeout"); + return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "matchTimeout"); } // Determine the namespace the class is declared in, if any - string? ns = regexMethodSymbol.ContainingType?.ContainingNamespace?.ToDisplayString( + string? ns = regexMemberSymbol.ContainingType?.ContainingNamespace?.ToDisplayString( SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)); var regexType = new RegexType( @@ -183,9 +213,11 @@ public partial class RegexGenerator var result = new RegexPatternAndSyntax( regexType, - GetComparableLocation(methodSyntax), - regexMethodSymbol.Name, - methodSyntax.Modifiers.ToString(), + IsProperty: regexMemberSymbol is IPropertySymbol, + GetComparableLocation(memberSyntax), + regexMemberSymbol.Name, + memberSyntax.Modifiers.ToString(), + nullableRegex, pattern, regexOptions, matchTimeout, @@ -217,18 +249,18 @@ SyntaxKind.RecordStructDeclaration or // Get a Location object that doesn't store a reference to the compilation. // That allows it to compare equally across compilations. - static Location GetComparableLocation(MethodDeclarationSyntax method) + static Location GetComparableLocation(SyntaxNode syntax) { - var location = method.GetLocation(); + var location = syntax.GetLocation(); return Location.Create(location.SourceTree?.FilePath ?? string.Empty, location.SourceSpan, location.GetLineSpan().Span); } } /// Data about a regex directly from the GeneratedRegex attribute. - internal sealed record RegexPatternAndSyntax(RegexType DeclaringType, Location DiagnosticLocation, string MethodName, string Modifiers, string Pattern, RegexOptions Options, int? MatchTimeout, CultureInfo Culture, CompilationData CompilationData); + internal sealed record RegexPatternAndSyntax(RegexType DeclaringType, bool IsProperty, Location DiagnosticLocation, string MemberName, string Modifiers, bool NullableRegex, string Pattern, RegexOptions Options, int? MatchTimeout, CultureInfo Culture, CompilationData CompilationData); /// Data about a regex, including a fully parsed RegexTree and subsequent analysis. - internal sealed record RegexMethod(RegexType DeclaringType, Location DiagnosticLocation, string MethodName, string Modifiers, string Pattern, RegexOptions Options, int? MatchTimeout, RegexTree Tree, AnalysisResults Analysis, CompilationData CompilationData) + internal sealed record RegexMethod(RegexType DeclaringType, bool IsProperty, Location DiagnosticLocation, string MemberName, string Modifiers, bool NullableRegex, string Pattern, RegexOptions Options, int? MatchTimeout, RegexTree Tree, AnalysisResults Analysis, CompilationData CompilationData) { public string? GeneratedName { get; set; } public bool IsDuplicate { get; set; } diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs index 5ed5eebc07331..09062dc78c249 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs @@ -57,7 +57,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // if there are no changes. .ForAttributeWithMetadataName( GeneratedRegexAttributeName, - (node, _) => node is MethodDeclarationSyntax, + (node, _) => node is MethodDeclarationSyntax or PropertyDeclarationSyntax or IndexerDeclarationSyntax or AccessorDeclarationSyntax, GetRegexMethodDataOrFailureDiagnostic) // Filter out any parsing errors that resulted in null objects being returned. @@ -73,7 +73,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { RegexTree regexTree = RegexParser.Parse(method.Pattern, method.Options | RegexOptions.Compiled, method.Culture); // make sure Compiled is included to get all optimizations applied to it AnalysisResults analysis = RegexTreeAnalyzer.Analyze(regexTree); - return new RegexMethod(method.DeclaringType, method.DiagnosticLocation, method.MethodName, method.Modifiers, method.Pattern, method.Options, method.MatchTimeout, regexTree, analysis, method.CompilationData); + return new RegexMethod(method.DeclaringType, method.IsProperty, method.DiagnosticLocation, method.MemberName, method.Modifiers, method.NullableRegex, method.Pattern, method.Options, method.MatchTimeout, regexTree, analysis, method.CompilationData); } catch (Exception e) { @@ -201,7 +201,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) else { regexMethod.IsDuplicate = false; - regexMethod.GeneratedName = $"{regexMethod.MethodName}_{id++}"; + regexMethod.GeneratedName = $"{regexMethod.MemberName}_{id++}"; emittedExpressions.Add(key, regexMethod); } diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/Strings.resx b/src/libraries/System.Text.RegularExpressions/gen/Resources/Strings.resx index 0b2904b0bfac0..730a2f05415fc 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/Strings.resx +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/Strings.resx @@ -131,8 +131,8 @@ The specified regex is invalid. '{0}' - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. Regex generator limitation reached. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.cs.xlf index d39c7ec6b56a6..0b181eed2a754 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.cs.xlf @@ -192,9 +192,9 @@ Vypršel časový limit modulu Regex při pokusu o porovnání vzoru se vstupním řetězcem. K tomu může dojít z celé řady důvodů, mezi které patří velká velikost vstupních dat nebo nadměrné zpětné navracení způsobené vloženými kvantifikátory, zpětnými odkazy a dalšími faktory. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - Metoda GeneratedRegexAttribute musí být částečná, bez parametrů, neobecná, neabstraktní a návratová metoda Regex. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + Metoda GeneratedRegexAttribute musí být částečná, bez parametrů, neobecná, neabstraktní a návratová metoda Regex. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.de.xlf index 8e267826a243a..3e257c863e4c5 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.de.xlf @@ -192,9 +192,9 @@ Zeitüberschreitung des RegEx-Moduls beim Versuch, ein Muster mit einer Eingabezeichenfolge in Übereinstimmung zu bringen. Dies kann viele Ursachen haben, darunter sehr große Eingaben oder übermäßige Rückverfolgung aufgrund von geschachtelten Quantifizierern, Rückverweisen und anderen Faktoren. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - Die GeneratedRegexAttribute-Methode muss partiell, parameterlos, nicht generisch, nicht abstrakt sein und RegEx zurückgeben. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + Die GeneratedRegexAttribute-Methode muss partiell, parameterlos, nicht generisch, nicht abstrakt sein und RegEx zurückgeben. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.es.xlf index e423171d0c07f..69d6170dece96 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.es.xlf @@ -192,9 +192,9 @@ Se agotó el tiempo de espera mientras el motor de Regex intentaba comparar una cadena de entrada con un patrón. Esto puede deberse a muchos motivos, como la especificación de cadenas de entrada muy grandes o búsquedas hacia atrás excesivas causadas por cuantificadores anidados, referencias inversas y otros factores. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - El método GeneratedRegexAttribute debe ser parcial, sin parámetros, no genérico, no abstracto y devolver Regex + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + El método GeneratedRegexAttribute debe ser parcial, sin parámetros, no genérico, no abstracto y devolver Regex diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.fr.xlf index dbc77bfb47685..6049ad85a67b6 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.fr.xlf @@ -192,9 +192,9 @@ Le délai d’attente du moteur Regex a expiré pendant la tentative de mise en correspondance d’un modèle avec une chaîne d’entrée. Ce problème peut se produire pour de nombreuses raisons, notamment en cas d’entrées volumineuses ou de retour sur trace excessif causé par les quantificateurs imbriqués, les références arrière et d’autres facteurs. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - La méthode GeneratedRegexAttribute doit être partielle, sans paramètre, non générique, non abstraite et renvoyer Regex. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + La méthode GeneratedRegexAttribute doit être partielle, sans paramètre, non générique, non abstraite et renvoyer Regex. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.it.xlf index 58b1524da3d7b..f03a43ef84f24 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.it.xlf @@ -192,9 +192,9 @@ Timeout del motore Regex durante il tentativo di trovare una corrispondenza tra un criterio e una stringa di input. Il timeout può verificarsi per diversi motivi, tra cui input molto grandi o un eccessivo backtracking causato da quantificatori annidati, backreference e altri fattori. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - Il metodo GeneratedRegexAttribute deve essere parziale, senza parametri, non generico, non astratto e restituire Regex. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + Il metodo GeneratedRegexAttribute deve essere parziale, senza parametri, non generico, non astratto e restituire Regex. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ja.xlf index ac87dd3a81363..c0b948e2989b6 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ja.xlf @@ -192,9 +192,9 @@ パターンと入力文字列との照合中に、Regex エンジンがタイムアウトしました。これは、非常に大きな入力、入れ子になった量指定子によって生じた過剰なバックトラッキング、前方参照などの要因を含む、さまざまな原因によって発生する可能性があります。 - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - GeneratedRegexAttribute メソッドは、部分、パラメーターなし、非ジェネリック、非抽象、および正規表現を返す必要があります。 + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + GeneratedRegexAttribute メソッドは、部分、パラメーターなし、非ジェネリック、非抽象、および正規表現を返す必要があります。 diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ko.xlf index b2c87b5648d59..8dece6af2fdcf 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ko.xlf @@ -192,9 +192,9 @@ Regex 엔진이 패턴을 입력 문자열에 일치시키는 동안 시간 초과되었습니다. 이 오류는 많은 입력, 중첩 수량자로 인한 과도한 역추적, 역참조, 기타 요인 등의 다양한 이유로 인해 발생할 수 있습니다. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - GeneratedRegexAttribute 메서드는 부분적이고 매개 변수가 없으며 제네릭이 아닌 비추상 메서드여야 하며 regex를 반환해야 합니다. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + GeneratedRegexAttribute 메서드는 부분적이고 매개 변수가 없으며 제네릭이 아닌 비추상 메서드여야 하며 regex를 반환해야 합니다. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pl.xlf index bd8c1ffc42832..ff70df54c702f 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pl.xlf @@ -192,9 +192,9 @@ Upłynął limit czasu podczas próby dopasowania przez aparat wyrażeń regularnych wzorca do ciągu wejściowego. Mogło to być spowodowane wieloma przyczynami, w tym bardzo dużą ilością danych wejściowych, nadmiernym wykorzystaniem algorytmu wycofywania związanym z kwantyfikatorami zagnieżdżonymi, odwołaniami wstecznymi i innymi czynnikami. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - Metoda GeneratedRegexAttribute musi być częściowa, bez parametrów, niestanowa, nieabstrakcyjna i zwracać wyrażenia regularne. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + Metoda GeneratedRegexAttribute musi być częściowa, bez parametrów, niestanowa, nieabstrakcyjna i zwracać wyrażenia regularne. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pt-BR.xlf index 4bfcf7e55eae3..9e080e4a390f4 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.pt-BR.xlf @@ -192,9 +192,9 @@ O mecanismo Regex atingiu o tempo limite ao tentar combinar um padrão com uma cadeia de caracteres de entrada. Isso pode ocorrer por vários motivos, incluindo entradas muito grandes ou retrocessos excessivos causados por quantificadores aninhados, referências anteriores e outros fatores. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - O método GeneratedRegexAttribute deve ser parcial, sem parâmetros, não genérico, não abstrato e retornar Regex. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + O método GeneratedRegexAttribute deve ser parcial, sem parâmetros, não genérico, não abstrato e retornar Regex. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ru.xlf index 4f8b97ac870f2..cb663cbd61a5a 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.ru.xlf @@ -192,9 +192,9 @@ Истекло время ожидания модуля регулярного выражения при попытке сравнить шаблон со входной строкой. Это могло произойти по многим причинам, в том числе из-за очень большого объема входных данных или излишнего обратного отслеживания, вызванного вложенными квантификаторами, обратными ссылками и прочими факторами. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - Метод GeneratedRegexAttribute должен быть частичным, без параметров, неуниверсальным, неабстрактным и возвращать регулярное выражение. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + Метод GeneratedRegexAttribute должен быть частичным, без параметров, неуниверсальным, неабстрактным и возвращать регулярное выражение. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.tr.xlf index 0f90dc4e1b9e9..ffad46d7c3a7d 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.tr.xlf @@ -192,9 +192,9 @@ Normal ifade altyapısı bir deseni bir giriş dizesiyle eşleştirmeye çalışırken zaman aşımına uğradı. Bu durum, çok büyük girişler veya iç içe niceleyiciler, geri başvurular ve diğer faktörler nedeniyle oluşan aşırı geri izleme gibi birçok nedenle oluşabilir. - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - GeneratedRegexAttribute yöntemi kısmi ve parametresiz olmalı, genel amaçlı veya soyut olmamalı ve Normal İfade döndürmelidir. + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + GeneratedRegexAttribute yöntemi kısmi ve parametresiz olmalı, genel amaçlı veya soyut olmamalı ve Normal İfade döndürmelidir. diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hans.xlf index ba9d314ccca2a..6d43631c5b716 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -192,9 +192,9 @@ 尝试将模式与输入字符串匹配时,Regex 引擎超时。许多原因均可能导致出现这种情况,包括由嵌套限定符、反向引用和其他因素引起的大量输入或过度回溯。 - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - GeneratedRegexAttribute 方法必须是分部、无参数、非泛型、非抽象且返回 Regex。 + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + GeneratedRegexAttribute 方法必须是分部、无参数、非泛型、非抽象且返回 Regex。 diff --git a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hant.xlf index 070b36b4125be..7e23491c00eed 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Text.RegularExpressions/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -192,9 +192,9 @@ 嘗試將模式對應至輸入字串時,Regex 引擎發生逾時。有很多原因會導致這個情形,其中包括巢狀數量詞、反向參考及其他因素所造成的極大量輸入或過度使用回溯法。 - - GeneratedRegexAttribute method must be partial, parameterless, non-generic, non-abstract, and return Regex. - GeneratedRegexAttribute 方法必須是部分、無參數、非泛型、非抽象,並且傳回 Regex。 + + GeneratedRegexAttribute method or property must be partial, parameterless, non-generic, non-abstract, and return Regex. If a property, it must also be get-only. + GeneratedRegexAttribute 方法必須是部分、無參數、非泛型、非抽象,並且傳回 Regex。 diff --git a/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs b/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs index 949f4939062bf..0730da675126c 100644 --- a/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs +++ b/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs @@ -256,7 +256,7 @@ public RegexCompilationInfo(string pattern, System.Text.RegularExpressions.Regex public System.Text.RegularExpressions.RegexOptions Options { get { throw null; } set { } } public string Pattern { get { throw null; } set { } } } - [System.AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Method | System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public sealed partial class GeneratedRegexAttribute : System.Attribute { public GeneratedRegexAttribute([System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.Regex)] string pattern) { } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs index 838527ebb78bd..9e41bb898336d 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GeneratedRegexAttribute.cs @@ -9,8 +9,8 @@ namespace System.Text.RegularExpressions; /// Instructs the System.Text.RegularExpressions source generator to generate an implementation of the specified regular expression. /// /// -/// The generator associated with this attribute only supports C#. It only supplies an implementation when applied to static, partial, parameterless, non-generic methods that -/// are typed to return . +/// The generator associated with this attribute only supports C#. It only supplies an implementation when applied to partial, parameterless, non-generic methods +/// or get-only properties that are typed to return . /// /// /// When the supports case-insensitive matches (either by passing or using the inline `(?i)` switch in the pattern) the regex @@ -21,7 +21,7 @@ namespace System.Text.RegularExpressions; /// they will always use casing table for the current runtime. /// /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public sealed class GeneratedRegexAttribute : Attribute { /// Initializes a new instance of the with the specified pattern. @@ -42,7 +42,7 @@ public GeneratedRegexAttribute([StringSyntax(StringSyntaxAttribute.Regex, nameof /// A bitwise combination of the enumeration values that modify the regular expression. /// The name of a culture to be used for case sensitive comparisons. is not case-sensitive. /// - /// For a list of predefined culture names on Windows systems, see the Language tag column in the list of + /// For a list of predefined culture names on Windows systems, see the Language tag column in the list of /// language/region names suported by Windows. Culture names follow the standard defined by BCP 47. In addition, /// starting with Windows 10, can be any valid BCP-47 language tag. /// @@ -66,7 +66,7 @@ public GeneratedRegexAttribute([StringSyntax(StringSyntaxAttribute.Regex, nameof /// A time-out interval (milliseconds), or to indicate that the method should not time out. /// The name of a culture to be used for case sensitive comparisons. is not case-sensitive. /// - /// For a list of predefined culture names on Windows systems, see the Language tag column in the list of + /// For a list of predefined culture names on Windows systems, see the Language tag column in the list of /// language/region names suported by Windows. Culture names follow the standard defined by BCP 47. In addition, /// starting with Windows 10, can be any valid BCP-47 language tag. /// diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs index 5cbd1008cdb00..ac4066a2f000a 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs @@ -228,10 +228,8 @@ internal static unsafe string SegmentsToStringAndDispose(ref StructListBuilder> tmpSpan = span; // avoid address exposing the span and impacting the other code in the method that uses it - string result = string.Create(length, (IntPtr)(&tmpSpan), static (dest, spanPtr) => + string result = string.Create(length, span, static (dest, span) => { - Span> span = *(Span>*)spanPtr; for (int i = 0; i < span.Length; i++) { ReadOnlySpan segment = span[i].Span; diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.KnownPattern.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.KnownPattern.Tests.cs index e7fe42f1cb165..d71726deea0c4 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.KnownPattern.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.KnownPattern.Tests.cs @@ -18,10 +18,10 @@ namespace System.Text.RegularExpressions.Tests public class RegexKnownPatternTests { // - // These patterns come from the Regex documentation at docs.microsoft.com. + // These patterns come from the Regex documentation at https://learn.microsoft.com. // - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-example-scanning-for-hrefs + // https://learn.microsoft.com/dotnet/standard/base-types/regular-expression-example-scanning-for-hrefs [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Examples_ScanningHrefs(RegexEngine engine) @@ -59,7 +59,7 @@ public async Task Docs_Examples_ScanningHrefs(RegexEngine engine) Assert.False(m.Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-example-changing-date-formats + // https://learn.microsoft.com/dotnet/standard/base-types/regular-expression-example-changing-date-formats [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Examples_MDYtoDMY(RegexEngine engine) @@ -70,7 +70,7 @@ public async Task Docs_Examples_MDYtoDMY(RegexEngine engine) Assert.Equal("08-01-2020", r.Replace(dt, "${day}-${month}-${year}")); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-extract-a-protocol-and-port-number-from-a-url + // https://learn.microsoft.com/dotnet/standard/base-types/how-to-extract-a-protocol-and-port-number-from-a-url [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Examples_ExtractProtocolPort(RegexEngine engine) @@ -108,7 +108,7 @@ public static IEnumerable Docs_Examples_ValidateEmail_TestData() } } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format + // https://learn.microsoft.com/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format [Theory] [MemberData(nameof(Docs_Examples_ValidateEmail_TestData))] public async Task Docs_Examples_ValidateEmail(RegexEngine engine, string email, bool expectedIsValid) @@ -153,7 +153,7 @@ async Task IsValidEmailAsync(string email, RegexEngine engine) } } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#matched_subexpression + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#matched_subexpression [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_MatchedSubexpression(RegexEngine engine) @@ -185,7 +185,7 @@ public async Task Docs_GroupingConstructs_MatchedSubexpression(RegexEngine engin Assert.False(match.NextMatch().Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#named-matched-subexpressions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#named-matched-subexpressions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_NamedMatchedSubexpression1(RegexEngine engine) @@ -217,7 +217,7 @@ public async Task Docs_GroupingConstructs_NamedMatchedSubexpression1(RegexEngine Assert.False(match.NextMatch().Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#named-matched-subexpressions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#named-matched-subexpressions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_NamedMatchedSubexpression2(RegexEngine engine) @@ -264,7 +264,7 @@ public async Task Docs_GroupingConstructs_NamedMatchedSubexpression2(RegexEngine Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#balancing-group-definitions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#balancing-group-definitions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_BalancingGroups(RegexEngine engine) @@ -331,7 +331,7 @@ public async Task Docs_GroupingConstructs_BalancingGroups(RegexEngine engine) Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#noncapturing-groups + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#noncapturing-groups [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_NoncapturingGroups(RegexEngine engine) @@ -347,7 +347,7 @@ public async Task Docs_GroupingConstructs_NoncapturingGroups(RegexEngine engine) Assert.Equal(1, match.Groups.Count); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#group-options + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#group-options [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_GroupOptions(RegexEngine engine) @@ -370,7 +370,7 @@ public async Task Docs_GroupingConstructs_GroupOptions(RegexEngine engine) Assert.False(match.NextMatch().Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-positive-lookahead-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-positive-lookahead-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_ZeroWidthPositiveLookaheadAssertions(RegexEngine engine) @@ -401,7 +401,7 @@ public async Task Docs_GroupingConstructs_ZeroWidthPositiveLookaheadAssertions(R Assert.Equal("Sunday", match.Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-negative-lookahead-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-negative-lookahead-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_ZeroWidthNegativeLookaheadAssertions(RegexEngine engine) @@ -424,7 +424,7 @@ public async Task Docs_GroupingConstructs_ZeroWidthNegativeLookaheadAssertions(R Assert.Equal("ultimate", matches[3].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-positive-lookbehind-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-positive-lookbehind-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_ZeroWidthPositiveLookbehindAssertions(RegexEngine engine) @@ -445,7 +445,7 @@ public async Task Docs_GroupingConstructs_ZeroWidthPositiveLookbehindAssertions( Assert.Equal("09", matches[1].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-negative-lookbehind-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#zero-width-negative-lookbehind-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_ZeroWidthNegativeLookbehindAssertions(RegexEngine engine) @@ -467,7 +467,7 @@ public async Task Docs_GroupingConstructs_ZeroWidthNegativeLookbehindAssertions( Assert.Equal("February 8, 2010", r.Match("Monday, February 8, 2010").Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#nonbacktracking-subexpressions + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#nonbacktracking-subexpressions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_NonbacktrackingSubexpressions(RegexEngine engine) @@ -503,7 +503,7 @@ public async Task Docs_GroupingConstructs_NonbacktrackingSubexpressions(RegexEng Assert.Equal("aaaa", back.Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#grouping-constructs-and-regular-expression-objects + // https://learn.microsoft.com/dotnet/standard/base-types/grouping-constructs-in-regular-expressions#grouping-constructs-and-regular-expression-objects [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_GroupingConstructs_GroupCaptureRelationship(RegexEngine engine) @@ -552,7 +552,7 @@ public async Task Docs_GroupingConstructs_GroupCaptureRelationship(RegexEngine e Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.capture?view=netcore-3.1#examples + // https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.capture?view=netcore-3.1#examples [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Capture_Sentences(RegexEngine engine) @@ -610,7 +610,7 @@ public async Task Docs_Capture_Sentences(RegexEngine engine) Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.capture.value?view=netcore-3.1 + // https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.capture.value?view=netcore-3.1 [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Capture_ProductNumber(RegexEngine engine) @@ -706,7 +706,7 @@ public async Task Docs_Capture_ProductNumber(RegexEngine engine) Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#linear-comparison-without-backtracking + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#linear-comparison-without-backtracking [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Backtracking_LinearComparisonWithoutBacktracking(RegexEngine engine) @@ -722,7 +722,7 @@ public async Task Docs_Backtracking_LinearComparisonWithoutBacktracking(RegexEng Assert.Equal(11, matches[0].Index); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#backtracking-with-optional-quantifiers-or-alternation-constructs + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#backtracking-with-optional-quantifiers-or-alternation-constructs [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Backtracking_WithOptionalQuantifiersOrAlternationConstructs(RegexEngine engine) @@ -741,7 +741,7 @@ public async Task Docs_Backtracking_WithOptionalQuantifiersOrAlternationConstruc Assert.False(m.NextMatch().Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#nonbacktracking-subexpression + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#nonbacktracking-subexpression [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Backtracking_WithNestedOptionalQuantifiers_BacktrackingEliminated(RegexEngine engine) @@ -755,7 +755,7 @@ public async Task Docs_Backtracking_WithNestedOptionalQuantifiers_BacktrackingEl Assert.False(r.IsMatch(Input)); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#lookbehind-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#lookbehind-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Backtracking_LookbehindAssertions(RegexEngine engine) @@ -775,7 +775,7 @@ public async Task Docs_Backtracking_LookbehindAssertions(RegexEngine engine) Assert.True(rBehindPattern.IsMatch(Input)); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#lookahead-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#lookahead-assertions [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Doesn't support NonBacktracking")] [Theory] [InlineData(RegexEngine.NonBacktracking)] @@ -785,7 +785,7 @@ public async Task Docs_Backtracking_LookaheadAssertions_ExcessiveBacktracking(Re Assert.False(r.IsMatch("aaaaaaaaaaaaaaaaaaaaaa.")); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions#lookahead-assertions + // https://learn.microsoft.com/dotnet/standard/base-types/backtracking-in-regular-expressions#lookahead-assertions [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Backtracking_LookaheadAssertions_BacktrackingEliminated(RegexEngine engine) @@ -800,7 +800,7 @@ public async Task Docs_Backtracking_LookaheadAssertions_BacktrackingEliminated(R Assert.False(r.IsMatch("aaaaaaaaaaaaaaaaaaaaaa.")); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities + // https://learn.microsoft.com/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_LazyQuantifiers(RegexEngine engine) @@ -818,7 +818,7 @@ public async Task Docs_EngineCapabilities_LazyQuantifiers(RegexEngine engine) Assert.Equal("107325", match.Groups[1].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities + // https://learn.microsoft.com/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_PositiveLookahead(RegexEngine engine) @@ -841,7 +841,7 @@ public async Task Docs_EngineCapabilities_PositiveLookahead(RegexEngine engine) Assert.Equal("comes", matches[2].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities + // https://learn.microsoft.com/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_NegativeLookahead(RegexEngine engine) @@ -865,7 +865,7 @@ public async Task Docs_EngineCapabilities_NegativeLookahead(RegexEngine engine) Assert.Equal("functional", matches[3].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/alternation-constructs-in-regular-expressions#conditional-matching-with-an-expression + // https://learn.microsoft.com/dotnet/standard/base-types/alternation-constructs-in-regular-expressions#conditional-matching-with-an-expression [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_ConditionalEvaluation(RegexEngine engine) @@ -891,7 +891,7 @@ public async Task Docs_EngineCapabilities_ConditionalEvaluation(RegexEngine engi Assert.Equal(22, matches[1].Index); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities + // https://learn.microsoft.com/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_RightToLeftMatching(RegexEngine engine) @@ -919,7 +919,7 @@ public async Task Docs_EngineCapabilities_RightToLeftMatching(RegexEngine engine Assert.Equal("107325", match.Groups[1].Value); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities + // https://learn.microsoft.com/dotnet/standard/base-types/details-of-regular-expression-behavior#net-framework-engine-capabilities [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EngineCapabilities_PositiveNegativeLookbehind(RegexEngine engine) @@ -941,7 +941,7 @@ public async Task Docs_EngineCapabilities_PositiveNegativeLookbehind(RegexEngine Assert.False(r.IsMatch("me.myself!")); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#inline-options + // https://learn.microsoft.com/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#inline-options [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_InlineOptions(RegexEngine engine) @@ -988,7 +988,7 @@ public async Task Docs_InlineOptions(RegexEngine engine) Assert.Equal(expected, actual.ToString()); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#inline-comment + // https://learn.microsoft.com/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#inline-comment [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_InlineComment(RegexEngine engine) @@ -1013,7 +1013,7 @@ public async Task Docs_InlineComment(RegexEngine engine) Assert.False(match.NextMatch().Success); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#end-of-line-comment + // https://learn.microsoft.com/dotnet/standard/base-types/miscellaneous-constructs-in-regular-expressions#end-of-line-comment [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_EndOfLineComment(RegexEngine engine) @@ -1026,7 +1026,7 @@ public async Task Docs_EndOfLineComment(RegexEngine engine) Assert.True(r.IsMatch(Input)); } - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/anchors-in-regular-expressions#contiguous-matches-g + // https://learn.microsoft.com/dotnet/standard/base-types/anchors-in-regular-expressions#contiguous-matches-g [Theory] [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Docs_Anchors_ContiguousMatches(RegexEngine engine) diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs index cd601fca94177..2eb52774c3384 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs @@ -359,7 +359,7 @@ public static IEnumerable UnicodeCategoriesInclusionsExpected_MemberDa { foreach (RegexEngine engine in RegexHelpers.AvailableEngines) { - // https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions#supported-unicode-general-categories + // https://learn.microsoft.com/dotnet/standard/base-types/character-classes-in-regular-expressions#supported-unicode-general-categories yield return new object[] { engine, "L", new[] { UnicodeCategory.UppercaseLetter, UnicodeCategory.LowercaseLetter, UnicodeCategory.TitlecaseLetter, UnicodeCategory.ModifierLetter, UnicodeCategory.OtherLetter } }; yield return new object[] { engine, "Lu", new[] { UnicodeCategory.UppercaseLetter } }; diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs index 489c4c164a642..fe0e793819f64 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs @@ -177,7 +177,9 @@ internal static async Task SourceGenRegexAsync( { code.Append($", {SymbolDisplay.FormatLiteral(regex.culture.Name, quote: true)}"); } - code.AppendLine($")] public static partial Regex Get{count}();"); + + bool useProp = count % 2 == 0; // validate both methods and properties by alternating between them + code.AppendLine($")] public static partial Regex Get{count}{(useProp ? " { get; }" : "();")}"); count++; } @@ -238,12 +240,13 @@ internal static async Task SourceGenRegexAsync( var alc = new RegexLoadContext(Environment.CurrentDirectory); Assembly a = alc.LoadFromStream(dll); - // Instantiate each regex using the newly created static Get method that was source generated. + // Instantiate each regex using the newly created static Get member that was source generated. var instances = new Regex[count]; Type c = a.GetType("C")!; for (int i = 0; i < instances.Length; i++) { - instances[i] = (Regex)c.GetMethod($"Get{i}")!.Invoke(null, null)!; + string memberName = $"Get{i}"; + instances[i] = (Regex)(c.GetMethod(memberName) ?? c.GetProperty(memberName).GetGetMethod())!.Invoke(null, null)!; } // Issue an unload on the ALC, so it'll be collected once the Regex instance is collected diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs index 28fd8bfbc3614..b4d46d8274f57 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs @@ -19,7 +19,7 @@ namespace System.Text.RegularExpressions.Tests public class RegexGeneratorParserTests { [Fact] - public async Task Diagnostic_MultipleAttributes() + public async Task Diagnostic_Method_MultipleAttributes() { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" using System.Text.RegularExpressions; @@ -34,6 +34,22 @@ partial class C Assert.Equal("SYSLIB1041", Assert.Single(diagnostics).Id); } + [Fact] + public async Task Diagnostic_Property_MultipleAttributes() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + [GeneratedRegex(""abc"")] + private static partial Regex MultipleAttributes { get; }; + } + "); + + Assert.Equal("SYSLIB1041", Assert.Single(diagnostics).Id); + } + public static IEnumerable Diagnostic_MalformedCtor_MemberData() { const string Pre = "[GeneratedRegex"; @@ -54,7 +70,7 @@ public static IEnumerable Diagnostic_MalformedCtor_MemberData() [Theory] [MemberData(nameof(Diagnostic_MalformedCtor_MemberData))] - public async Task Diagnostic_MalformedCtor(string attribute) + public async Task Diagnostic_Method_MalformedCtor(string attribute) { // Validate the generator doesn't crash with an incomplete attribute @@ -73,10 +89,31 @@ partial class C } } + [Theory] + [MemberData(nameof(Diagnostic_MalformedCtor_MemberData))] + public async Task Diagnostic_Property_MalformedCtor(string attribute) + { + // Validate the generator doesn't crash with an incomplete attribute + + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator($@" + using System.Text.RegularExpressions; + partial class C + {{ + {attribute} + private static partial Regex MultipleAttributes {{ get; }}; + }} + "); + + if (diagnostics.Count != 0) + { + Assert.Contains(Assert.Single(diagnostics).Id, new[] { "SYSLIB1040", "SYSLIB1042" }); + } + } + [Theory] [InlineData("null")] [InlineData("\"ab[]\"")] - public async Task Diagnostic_InvalidRegexPattern(string pattern) + public async Task Diagnostic_Method_InvalidRegexPattern(string pattern) { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator($@" using System.Text.RegularExpressions; @@ -90,9 +127,26 @@ partial class C Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); } + [Theory] + [InlineData("null")] + [InlineData("\"ab[]\"")] + public async Task Diagnostic_Property_InvalidRegexPattern(string pattern) + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator($@" + using System.Text.RegularExpressions; + partial class C + {{ + [GeneratedRegex({pattern})] + private static partial Regex InvalidPattern {{ get; }}; + }} + "); + + Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); + } + [Theory] [InlineData(0x800)] - public async Task Diagnostic_InvalidRegexOptions(int options) + public async Task Diagnostic_Method_InvalidRegexOptions(int options) { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" using System.Text.RegularExpressions; @@ -106,10 +160,26 @@ partial class C Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); } + [Theory] + [InlineData(0x800)] + public async Task Diagnostic_Property_InvalidRegexOptions(int options) + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" + using System.Text.RegularExpressions; + partial class C + {{ + [GeneratedRegex(""ab"", (RegexOptions){options})] + private static partial Regex InvalidPattern {{ get; }}; + }} + "); + + Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); + } + [Theory] [InlineData(-2)] [InlineData(0)] - public async Task Diagnostic_InvalidRegexTimeout(int matchTimeout) + public async Task Diagnostic_Method_InvalidRegexTimeout(int matchTimeout) { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" using System.Text.RegularExpressions; @@ -123,10 +193,27 @@ partial class C Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); } + [Theory] + [InlineData(-2)] + [InlineData(0)] + public async Task Diagnostic_Property_InvalidRegexTimeout(int matchTimeout) + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" + using System.Text.RegularExpressions; + partial class C + {{ + [GeneratedRegex(""ab"", RegexOptions.None, {matchTimeout.ToString(CultureInfo.InvariantCulture)})] + private static partial Regex InvalidPattern {{ get; }}; + }} + "); + + Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); + } + [Theory] [InlineData("null")] [InlineData("\"xxxxxxxxxxxxxxxxxxxx-ThisIsNotAValidCultureName-xxxxxxxxxxxxxxxxxxxx\"")] - public async Task Diagnostic_InvalidCultureName(string cultureName) + public async Task Diagnostic_Method_InvalidCultureName(string cultureName) { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" using System.Text.RegularExpressions; @@ -140,10 +227,27 @@ partial class C Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); } + [Theory] + [InlineData("null")] + [InlineData("\"xxxxxxxxxxxxxxxxxxxx-ThisIsNotAValidCultureName-xxxxxxxxxxxxxxxxxxxx\"")] + public async Task Diagnostic_Property_InvalidCultureName(string cultureName) + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" + using System.Text.RegularExpressions; + partial class C + {{ + [GeneratedRegex(""(?i)ab"", RegexOptions.None, {cultureName})] + private static partial Regex InvalidPattern {{ get; }}; + }} + "); + + Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); + } + [Theory] [InlineData("(?i)abc", "RegexOptions.CultureInvariant")] [InlineData("abc", "RegexOptions.CultureInvariant | RegexOptions.IgnoreCase")] - public async Task Diagnostic_InvalidOptionsForCaseInsensitive(string pattern, string options) + public async Task Diagnostic_Method_InvalidOptionsForCaseInsensitive(string pattern, string options) { IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" using System.Text.RegularExpressions; @@ -157,6 +261,23 @@ partial class C Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); } + [Theory] + [InlineData("(?i)abc", "RegexOptions.CultureInvariant")] + [InlineData("abc", "RegexOptions.CultureInvariant | RegexOptions.IgnoreCase")] + public async Task Diagnostic_Property_InvalidOptionsForCaseInsensitive(string pattern, string options) + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@$" + using System.Text.RegularExpressions; + partial class C + {{ + [GeneratedRegex(""{pattern}"", {options}, ""en-US"")] + private static partial Regex InvalidPattern {{ get; }}; + }} + "); + + Assert.Equal("SYSLIB1042", Assert.Single(diagnostics).Id); + } + [Fact] public async Task Diagnostic_MethodMustReturnRegex() { @@ -172,6 +293,21 @@ partial class C Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); } + [Fact] + public async Task Diagnostic_PropertyMustReturnRegex() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + private static partial int MethodMustReturnRegex { get; }; + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + [Fact] public async Task Diagnostic_MethodMustNotBeGeneric() { @@ -202,6 +338,21 @@ partial class C Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); } + [Fact] + public async Task Diagnostic_PropertyMustBeParameterless() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + private static partial Regex this[int i] { get; } + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + [Fact] public async Task Diagnostic_MethodMustBePartial() { @@ -217,6 +368,65 @@ partial class C Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); } + [Fact] + public async Task Diagnostic_PropertyMustBePartial() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + private static Regex MethodMustBePartial => null; + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + + [Fact] + public async Task Diagnostic_PropertyMustNotHaveSetter() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + private static partial Regex MethodMustBePartial { get; set; } + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + + [Fact] + public async Task Diagnostic_PropertyMustHaveGetter() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex(""ab"")] + private static partial Regex MethodMustBePartial { set; } + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + + [Fact] + public async Task Diagnostic_AttributeMustNotBeOnAccessor() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + partial class C + { + private static partial Regex MethodMustBePartial { [GeneratedRegex(""ab"")] get; } + } + "); + + Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); + } + [Fact] public async Task Diagnostic_MethodMustBeNonAbstract() { @@ -240,6 +450,29 @@ partial interface I Assert.All(diagnostics, d => Assert.Equal("SYSLIB1043", d.Id)); } + [Fact] + public async Task Diagnostic_PropertyMustBeNonAbstract() + { + IReadOnlyList diagnostics = await RegexGeneratorHelper.RunGenerator(@" + using System.Text.RegularExpressions; + + partial class C + { + [GeneratedRegex(""ab"")] + public abstract partial Regex MethodMustBeNonAbstract { get; }; + } + + partial interface I + { + [GeneratedRegex(""ab"")] + public static abstract partial Regex MethodMustBeNonAbstract { get; }; + } + "); + + Assert.Equal(2, diagnostics.Count); + Assert.All(diagnostics, d => Assert.Equal("SYSLIB1043", d.Id)); + } + [Theory] [InlineData(LanguageVersion.CSharp9)] [InlineData(LanguageVersion.CSharp10)] @@ -265,7 +498,7 @@ public async Task Diagnostic_NonBacktracking_LimitedSupport() partial class C { [GeneratedRegex(""ab"", RegexOptions.NonBacktracking)] - private static partial Regex RightToLeftNotSupported(); + private static partial Regex RightToLeftNotSupported { get; } } "); @@ -280,7 +513,7 @@ public async Task Diagnostic_CaseInsensitiveBackreference_LimitedSupport() partial class C { [GeneratedRegex(@""(?i)(ab)\1"")] - private static partial Regex CaseInsensitiveBackreferenceNotSupported(); + private static partial Regex CaseInsensitiveBackreferenceNotSupported { get; } } "); @@ -357,7 +590,10 @@ public async Task Valid_ClassWithoutNamespace() partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } ", compile: true)); } @@ -373,7 +609,10 @@ public async Task Valid_PatternOptions(string options) partial class C {{ [GeneratedRegex(""ab"", {options})] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"", {options})] + private static partial Regex Valid2 {{ get; }} }} ", compile: true)); } @@ -389,7 +628,10 @@ public async Task Valid_PatternOptionsTimeout(string timeout) partial class C {{ [GeneratedRegex(""ab"", RegexOptions.None, {timeout})] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"", RegexOptions.None, {timeout})] + private static partial Regex Valid2 {{ get; }} }} ", compile: true)); } @@ -402,7 +644,10 @@ public async Task Valid_NamedArguments() partial class C {{ [GeneratedRegex(pattern: ""ab"", options: RegexOptions.None, matchTimeoutMilliseconds: -1)] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(pattern: ""ab"", options: RegexOptions.None, matchTimeoutMilliseconds: -1)] + private static partial Regex Valid2 {{ get; }} }} ", compile: true)); } @@ -419,6 +664,12 @@ partial class C [GeneratedRegex(matchTimeoutMilliseconds: -1, pattern: ""ab"", options: RegexOptions.None)] private static partial Regex Valid2(); + + [GeneratedRegex(options: RegexOptions.None, matchTimeoutMilliseconds: -1, pattern: ""ab"")] + private static partial Regex Valid3 {{ get; }} + + [GeneratedRegex(matchTimeoutMilliseconds: -1, pattern: ""ab"", options: RegexOptions.None)] + private static partial Regex Valid4 {{ get; }} }} ", compile: true)); } @@ -434,7 +685,12 @@ partial class C [SuppressMessage(""CATEGORY1"", ""SOMEID1"")] [GeneratedRegex(""abc"")] [SuppressMessage(""CATEGORY2"", ""SOMEID2"")] - private static partial Regex AdditionalAttributes(); + private static partial Regex AdditionalAttributes1(); + + [SuppressMessage(""CATEGORY1"", ""SOMEID1"")] + [GeneratedRegex(""abc"")] + [SuppressMessage(""CATEGORY2"", ""SOMEID2"")] + private static partial Regex AdditionalAttributes2 {{ get; }} }} ", compile: true)); } @@ -457,7 +713,10 @@ namespace A partial class C {{ [GeneratedRegex(""{pattern}"", RegexOptions.{options})] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""{pattern}"", RegexOptions.{options})] + private static partial Regex Valid2 {{ get; }} }} }} ", compile: true, allowUnsafe: allowUnsafe, checkOverflow: checkOverflow)); @@ -472,7 +731,10 @@ namespace A; partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } ", compile: true)); } @@ -489,7 +751,10 @@ namespace B partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } } @@ -506,7 +771,10 @@ partial class B partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } ", compile: true)); @@ -524,7 +792,10 @@ partial class B partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } } @@ -542,7 +813,10 @@ partial class B partial class C { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } ", compile: true)); @@ -566,7 +840,10 @@ private protected partial class E private partial class F { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } } @@ -581,11 +858,31 @@ public async Task Valid_NullableRegex() { Assert.Empty(await RegexGeneratorHelper.RunGenerator(@" #nullable enable + using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; partial class C { [GeneratedRegex(""ab"")] - private static partial Regex? Valid(); + private static partial Regex? Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex? Valid2 { get; } + + [GeneratedRegex(""ab"")] + [MaybeNull] + private static partial Regex Valid3 { get; } + + [GeneratedRegex(""ab"")] + [MaybeNull] + private static partial Regex? Valid4 { get; } + + [GeneratedRegex(""ab"")] + [NotNull] + private static partial Regex Vali5 { get; } + + [GeneratedRegex(""ab"")] + [NotNull] + private static partial Regex? Valid6 { get; } } ", compile: true)); } @@ -603,7 +900,10 @@ public partial class B private partial class C where T : IBlah { [GeneratedRegex(""ab"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + private static partial Regex Valid2 { get; } } } } @@ -623,25 +923,37 @@ public async Task Valid_InterfaceStatics() partial interface INonGeneric { [GeneratedRegex("".+?"")] - public static partial Regex Test(); + public static partial Regex Test1(); + + [GeneratedRegex("".+?"")] + public static partial Regex Test2 { get; } } partial interface IGeneric { [GeneratedRegex("".+?"")] - public static partial Regex Test(); + public static partial Regex Test1(); + + [GeneratedRegex("".+?"")] + public static partial Regex Test2 { get; } } partial interface ICovariantGeneric { [GeneratedRegex("".+?"")] - public static partial Regex Test(); + public static partial Regex Test1(); + + [GeneratedRegex("".+?"")] + public static partial Regex Test2 { get; } } partial interface IContravariantGeneric { [GeneratedRegex("".+?"")] - public static partial Regex Test(); + public static partial Regex Test1(); + + [GeneratedRegex("".+?"")] + public static partial Regex Test2 { get; } } ", compile: true)); } @@ -655,13 +967,16 @@ public async Task Valid_VirtualBaseImplementations() partial class C { [GeneratedRegex(""ab"")] - public virtual partial Regex Valid(); + public virtual partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + public virtual partial Regex Valid2 { get; } } ", compile: true)); } [Fact] - public async Task Valid_SameMethodNameInMultipleTypes() + public async Task Valid_SameMemberNameInMultipleTypes() { Assert.Empty(await RegexGeneratorHelper.RunGenerator(@" using System.Text.RegularExpressions; @@ -672,25 +987,37 @@ public partial class B private partial class C { [GeneratedRegex(""1"")] - public partial Regex Valid(); + public partial Regex Valid1(); + + [GeneratedRegex(""1"")] + public partial Regex Valid2 { get; } } private partial class C { [GeneratedRegex(""2"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""2"")] + private static partial Regex Valid2 { get; } private partial class D { [GeneratedRegex(""3"")] - internal partial Regex Valid(); + internal partial Regex Valid1(); + + [GeneratedRegex(""3"")] + internal partial Regex Valid2 { get; } } } private partial class E { [GeneratedRegex(""4"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""4"")] + private static partial Regex Valid2 { get; } } } } @@ -702,6 +1029,9 @@ partial class F [GeneratedRegex(""6"")] public partial Regex Valid2(); + + [GeneratedRegex(""7"")] + public partial Regex Valid3 { get; } } ", compile: true)); } @@ -744,13 +1074,16 @@ public async Task Valid_Modifiers(string type, string typeModifier, bool instanc {typeModifier} partial {type} C {{ [GeneratedRegex(""ab"")] - {methodVisibility} {(instance ? "" : "static")} partial Regex Valid(); + {methodVisibility} {(instance ? "" : "static")} partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + {methodVisibility} {(instance ? "" : "static")} partial Regex Valid2 {{ get; }} }} ", compile: true)); } [Fact] - public async Task Valid_MultiplRegexMethodsPerClass() + public async Task Valid_MultipleRegexMembersPerClass() { Assert.Empty(await RegexGeneratorHelper.RunGenerator(@" using System.Text.RegularExpressions; @@ -764,14 +1097,23 @@ partial class C1 [GeneratedRegex(""b"")] public static partial Regex C(); + + [GeneratedRegex(""c"")] + public static partial Regex D { get; } + + [GeneratedRegex(""c"")] + public static partial Regex E { get; } + + [GeneratedRegex(""d"")] + public static partial Regex F { get; } } partial class C2 { - [GeneratedRegex(""d"")] - public static partial Regex D(); + [GeneratedRegex(""e"")] + public static partial Regex G(); - [GeneratedRegex(""d"")] - public static partial Regex E(); + [GeneratedRegex(""e"")] + public static partial Regex H(); } ", compile: true)); } @@ -792,7 +1134,10 @@ public partial record D public partial struct E { [GeneratedRegex(""ab"")] - public static partial Regex Valid(); + public static partial Regex Valid1(); + + [GeneratedRegex(""ab"")] + public static partial Regex Valid2 { get; } } } } @@ -818,7 +1163,10 @@ public GeneratedRegexAttribute(string pattern){} partial class C { [GeneratedRegex(""abc"")] - private static partial Regex Valid(); + private static partial Regex Valid1(); + + [GeneratedRegex(""abc"")] + private static partial Regex Valid2 { get; } }", compile: true, additionalRefs: new[] { MetadataReference.CreateFromImage(referencedAssembly) })); } @@ -831,7 +1179,10 @@ public async Task Valid_ConcatenatedLiteralsArgument() partial class C { [GeneratedRegex(""ab"" + ""[cd]"")] - public static partial Regex Valid(); + public static partial Regex Valid1(); + + [GeneratedRegex(""ab"" + ""[cd]"")] + public static partial Regex Valid2 { get; } } ", compile: true)); } @@ -845,7 +1196,10 @@ public async Task Valid_InterpolatedLiteralsArgument() partial class C { [GeneratedRegex($""{""ab""}{""cd""}"")] - public static partial Regex Valid(); + public static partial Regex Valid1(); + + [GeneratedRegex($""{""ab""}{""cd""}"")] + public static partial Regex Valid2 { get; } }", compile: true)); } diff --git a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/EventWaitHandleSecurity.cs b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/EventWaitHandleSecurity.cs index 222df279a5ee7..88a96bee4858a 100644 --- a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/EventWaitHandleSecurity.cs +++ b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/EventWaitHandleSecurity.cs @@ -21,7 +21,7 @@ namespace System.Security.AccessControl { // Derive this list of values from winnt.h and MSDN docs: - // https://docs.microsoft.com/en-us/windows/desktop/sync/synchronization-object-security-and-access-rights + // https://learn.microsoft.com/windows/desktop/sync/synchronization-object-security-and-access-rights // Win32's interesting values are EVENT_MODIFY_STATE (0x0002) and // EVENT_ALL_ACCESS (0x1F0003). I don't know what 0x1 is, but Windows diff --git a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/MutexSecurity.cs b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/MutexSecurity.cs index 26970fdf6aa7e..c775aed0dd66c 100644 --- a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/MutexSecurity.cs +++ b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/MutexSecurity.cs @@ -21,7 +21,7 @@ namespace System.Security.AccessControl { // Derive this list of values from winnt.h and MSDN docs: - // https://docs.microsoft.com/en-us/windows/desktop/sync/synchronization-object-security-and-access-rights + // https://learn.microsoft.com/windows/desktop/sync/synchronization-object-security-and-access-rights // In order to call ReleaseMutex, you must have an ACL granting you // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value diff --git a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/SemaphoreSecurity.cs b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/SemaphoreSecurity.cs index 34597415e481f..1cd2b7fffb168 100644 --- a/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/SemaphoreSecurity.cs +++ b/src/libraries/System.Threading.AccessControl/src/System/Security/AccessControl/SemaphoreSecurity.cs @@ -20,7 +20,7 @@ namespace System.Security.AccessControl { // Derive this list of values from winnt.h and MSDN docs: - // https://docs.microsoft.com/en-us/windows/desktop/sync/synchronization-object-security-and-access-rights + // https://learn.microsoft.com/windows/desktop/sync/synchronization-object-security-and-access-rights // Win32's interesting values are SEMAPHORE_MODIFY_STATE (0x2) and // SEMAPHORE_ALL_ACCESS (0x1F0003). I don't know what 0x1 is. diff --git a/src/libraries/System.Threading.AccessControl/tests/EventWaitHandleAclTests.cs b/src/libraries/System.Threading.AccessControl/tests/EventWaitHandleAclTests.cs index 3e9324e442f97..61cd59035b780 100644 --- a/src/libraries/System.Threading.AccessControl/tests/EventWaitHandleAclTests.cs +++ b/src/libraries/System.Threading.AccessControl/tests/EventWaitHandleAclTests.cs @@ -83,7 +83,7 @@ public void EventWaitHandle_Create_GlobalPrefixNameNotFound() } // The documentation says MAX_PATH is the length limit for name, but it won't throw any errors: - // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventexw + // https://learn.microsoft.com/windows/win32/api/synchapi/nf-synchapi-createeventexw // The .NET Core constructors for EventWaitHandle do not throw on name longer than MAX_LENGTH, so the extension method should match the behavior: // https://source.dot.net/#System.Private.CoreLib/shared/System/Threading/EventWaitHandle.Windows.cs,20 // The .NET Framework constructor throws: diff --git a/src/libraries/System.Threading.Tasks.Parallel/tests/BreakTests.cs b/src/libraries/System.Threading.Tasks.Parallel/tests/BreakTests.cs index 526f944a10350..6cfec4f9e742d 100644 --- a/src/libraries/System.Threading.Tasks.Parallel/tests/BreakTests.cs +++ b/src/libraries/System.Threading.Tasks.Parallel/tests/BreakTests.cs @@ -140,7 +140,7 @@ public static void TestForEach_Break(int loopsize, int breakpoint) Parallel.ForEach(mop, delegate(int item, ParallelLoopState ps, long index) { //break does not imply that the other iterations will not be run - //https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallelloopstate.break#System_Threading_Tasks_ParallelLoopState_Break + //https://learn.microsoft.com/dotnet/api/system.threading.tasks.parallelloopstate.break#System_Threading_Tasks_ParallelLoopState_Break //execute the test with high loop size and low break index complete[index] = true; if (index >= breakpoint) ps.Break(); diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs index 70a23d235a5a8..5df6a795b29c9 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IPrepareInfo.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686533(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686533(v=vs.85) [GeneratedComInterface, Guid("80c7bfd0-87ee-11ce-8081-0080c758527e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface IPrepareInfo { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs index b10be08d5f43d..f47e892dc2e57 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManager.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms681790(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms681790(v=vs.85) [GeneratedComInterface, Guid(Guids.IID_IResourceManager), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface IResourceManager { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs index e29ed9a7a5bf0..02b477e37002b 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerFactory2.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) [GeneratedComInterface, Guid("6B369C21-FBD2-11d1-8F47-00C04F8EE57D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface IResourceManagerFactory2 { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs index b39510bb57e24..641a9346cac38 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/IResourceManagerSink.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686073(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686073(v=vs.85) [GeneratedComInterface, Guid("0D563181-DEFB-11CE-AED1-00AA0051E2C4"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface IResourceManagerSink { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs index 4b6b4df3aab3c..6782c22f437cb 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITmNodeName.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms687122(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms687122(v=vs.85) [GeneratedComInterface, Guid("30274F88-6EE4-474e-9B95-7807BC9EF8CF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITmNodeName { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs index 6a162124f79ad..248a47eb24e32 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransaction.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686531(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686531(v=vs.85) [GeneratedComInterface, Guid(Guids.IID_ITransaction), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransaction { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs index 3a513b69ecd13..f688a0340faf9 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionCloner.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms684377(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms684377(v=vs.85) [GeneratedComInterface, Guid("02656950-2152-11d0-944C-00A0C905416E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionCloner { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs index 8d0a1d7c2ed82..461403d2fb555 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionDispenser.cs @@ -8,7 +8,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms687604(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms687604(v=vs.85) [GeneratedComInterface, Guid(Guids.IID_ITransactionDispenser), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionDispenser { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs index e5a2d36b2cd8c..b0862b8410f48 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionEnlistmentAsync.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686429(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686429(v=vs.85) [GeneratedComInterface, Guid("0fb15081-af41-11ce-bd2b-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionEnlistmentAsync { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs index 7fd2822a719da..0b557693d3dab 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExport.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms678954(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms678954(v=vs.85) [GeneratedComInterface, Guid("0141fda5-8fc0-11ce-bd18-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionExport { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs index 672f4576cb076..48ae5356b70bb 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionExportFactory.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686771(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686771(v=vs.85) [GeneratedComInterface, Guid("E1CF9B53-8745-11ce-A9BA-00AA006C3706"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionExportFactory { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs index 57d4656e878b1..9246a6de83af6 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImport.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms681296(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms681296(v=vs.85) [GeneratedComInterface, Guid("E1CF9B5A-8745-11ce-A9BA-00AA006C3706"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionImport { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs index 58b3d2f4b0721..e88c4a381f1c3 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionImportWhereabouts.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms682783(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms682783(v=vs.85) [GeneratedComInterface, Guid("0141fda4-8fc0-11ce-bd18-204c4f4f5020"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionImportWhereabouts { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs index 3a894ecfc0af2..2984dbe575e28 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOptions.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686489(v=vs.85) [GeneratedComInterface, Guid("3A6AD9E0-23B9-11cf-AD60-00AA00A74CCD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal partial interface ITransactionOptions { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs index 6d26bbba05aed..bafb3a79a02ad 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionOutcomeEvents.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; // Keep this type as a ComImport type as it is used with IConnectionPointContainer (and as a result needs to use built-in COM). -// https://docs.microsoft.com/previous-versions/windows/desktop/ms686465(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms686465(v=vs.85) [ComImport, Guid("3A6AD9E2-23B9-11cf-AD60-00AA00A74CCD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ITransactionOutcomeEvents { diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs index c77761e188ce9..3ccb0220e30a1 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/DtcInterfaces/ITransactionPhase0EnlistmentAsync.cs @@ -6,7 +6,7 @@ namespace System.Transactions.DtcProxyShim.DtcInterfaces; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms685087(v=vs.85). _eventHandle = notificationEventHandle; - // https://docs.microsoft.com/previous-versions/windows/desktop/ms678898(v=vs.85) + // https://learn.microsoft.com/previous-versions/windows/desktop/ms678898(v=vs.85) [LibraryImport(Interop.Libraries.Xolehlp, StringMarshalling = StringMarshalling.Utf16)] [RequiresUnreferencedCode(TransactionManager.DistributedTransactionTrimmingWarning)] private static unsafe partial int DtcGetTransactionManagerExW( diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs index 338df4590239c..fa4fe6c0c85d5 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/DtcProxyShim/Xactopt.cs @@ -7,7 +7,7 @@ namespace System.Transactions.DtcProxyShim; -// https://docs.microsoft.com/previous-versions/windows/desktop/ms679195(v=vs.85) +// https://learn.microsoft.com/previous-versions/windows/desktop/ms679195(v=vs.85) [NativeMarshalling(typeof(Marshaller))] [StructLayout(LayoutKind.Sequential)] internal struct Xactopt diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs index 9405c39a75bd6..58dfcd39a9bdf 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs @@ -117,7 +117,7 @@ public static NameValueCollection ParseQueryString(string query, Encoding encodi } else { - name = UrlDecode(query.Substring(namePos, valuePos - namePos - 1), encoding); + name = UrlDecode(query.AsSpan(namePos, valuePos - namePos - 1), encoding); } if (valueEnd < 0) @@ -126,7 +126,7 @@ public static NameValueCollection ParseQueryString(string query, Encoding encodi } namePos = valueEnd + 1; - string value = UrlDecode(query.Substring(valuePos, valueEnd - valuePos), encoding); + string value = UrlDecode(query.AsSpan(valuePos, valueEnd - valuePos), encoding); result.Add(name, value); } @@ -193,25 +193,33 @@ public static NameValueCollection ParseQueryString(string query, Encoding encodi public static byte[]? UrlDecodeToBytes(string? str) => UrlDecodeToBytes(str, Encoding.UTF8); [return: NotNullIfNotNull(nameof(str))] - public static byte[]? UrlDecodeToBytes(string? str, Encoding e) => str == null ? null : UrlDecodeToBytes(e.GetBytes(str)); - - [return: NotNullIfNotNull(nameof(bytes))] - public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : UrlDecodeToBytes(bytes, 0, bytes.Length); - - [return: NotNullIfNotNull(nameof(str))] - public static byte[]? UrlEncodeToBytes(string? str, Encoding e) + public static byte[]? UrlDecodeToBytes(string? str, Encoding e) { + const int StackallocThreshold = 512; if (str == null) { return null; } - byte[] bytes = e.GetBytes(str); - return HttpEncoder.UrlEncode(bytes, 0, bytes.Length, alwaysCreateNewReturnValue: false); + if (e.GetMaxByteCount(str.Length) <= StackallocThreshold) + { + Span bytes = stackalloc byte[StackallocThreshold]; + int encodedBytes = e.GetBytes(str, bytes); + + return HttpEncoder.UrlDecode(bytes.Slice(0, encodedBytes)); + } + + return UrlDecodeToBytes(e.GetBytes(str)); } [return: NotNullIfNotNull(nameof(bytes))] - public static byte[]? UrlEncodeToBytes(byte[]? bytes, int offset, int count) => HttpEncoder.UrlEncode(bytes, offset, count, alwaysCreateNewReturnValue: true); + public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : HttpEncoder.UrlDecode(bytes); + + [return: NotNullIfNotNull(nameof(str))] + public static byte[]? UrlEncodeToBytes(string? str, Encoding e) => str == null ? null : HttpEncoder.UrlEncode(str, e); + + [return: NotNullIfNotNull(nameof(bytes))] + public static byte[]? UrlEncodeToBytes(byte[]? bytes, int offset, int count) => HttpEncoder.UrlEncode(bytes, offset, count); [Obsolete("This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncode(String).")] [return: NotNullIfNotNull(nameof(str))] @@ -220,6 +228,8 @@ public static NameValueCollection ParseQueryString(string query, Encoding encodi [return: NotNullIfNotNull(nameof(str))] public static string? UrlDecode(string? str, Encoding e) => HttpEncoder.UrlDecode(str, e); + private static string UrlDecode(ReadOnlySpan str, Encoding e) => HttpEncoder.UrlDecode(str, e); + [return: NotNullIfNotNull(nameof(bytes))] public static string? UrlDecode(byte[]? bytes, int offset, int count, Encoding e) => HttpEncoder.UrlDecode(bytes, offset, count, e); diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs index 7220aa323ac20..3c96060bd20a9 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.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.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -12,6 +13,13 @@ namespace System.Web.Util { internal static class HttpEncoder { + private const int MaxStackAllocUrlLength = 256; + private const int StackallocThreshold = 512; + + // Set of safe chars, from RFC 1738.4 minus '+' + private static readonly SearchValues s_urlSafeBytes = SearchValues.Create( + "!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"u8); + private static void AppendCharAsUnicodeJavaScript(StringBuilder builder, char c) { builder.Append($"\\u{(int)c:x4}"); @@ -212,13 +220,18 @@ internal static string JavaScriptStringEncode(string? value) return null; } + return UrlDecode(bytes.AsSpan(offset, count)); + } + + internal static byte[] UrlDecode(ReadOnlySpan bytes) + { int decodedBytesCount = 0; - byte[] decodedBytes = new byte[count]; + int count = bytes.Length; + Span decodedBytes = count <= StackallocThreshold ? stackalloc byte[StackallocThreshold] : new byte[count]; for (int i = 0; i < count; i++) { - int pos = offset + i; - byte b = bytes[pos]; + byte b = bytes[i]; if (b == '+') { @@ -226,8 +239,8 @@ internal static string JavaScriptStringEncode(string? value) } else if (b == '%' && i < count - 2) { - int h1 = HexConverter.FromChar(bytes[pos + 1]); - int h2 = HexConverter.FromChar(bytes[pos + 2]); + int h1 = HexConverter.FromChar(bytes[i + 1]); + int h2 = HexConverter.FromChar(bytes[i + 2]); if ((h1 | h2) != 0xFF) { @@ -240,14 +253,7 @@ internal static string JavaScriptStringEncode(string? value) decodedBytes[decodedBytesCount++] = b; } - if (decodedBytesCount < decodedBytes.Length) - { - byte[] newDecodedBytes = new byte[decodedBytesCount]; - Array.Copy(decodedBytes, newDecodedBytes, decodedBytesCount); - decodedBytes = newDecodedBytes; - } - - return decodedBytes; + return decodedBytes.Slice(0, decodedBytesCount).ToArray(); } [return: NotNullIfNotNull(nameof(bytes))] @@ -258,7 +264,9 @@ internal static string JavaScriptStringEncode(string? value) return null; } - UrlDecoder helper = new UrlDecoder(count, encoding); + UrlDecoder helper = count <= MaxStackAllocUrlLength + ? new UrlDecoder(stackalloc char[MaxStackAllocUrlLength], stackalloc byte[MaxStackAllocUrlLength], encoding) + : new UrlDecoder(new char[count], new byte[count], encoding); // go through the bytes collapsing %XX and %uXXXX and appending // each byte as byte, with exception of %uXXXX constructs that @@ -321,8 +329,20 @@ internal static string JavaScriptStringEncode(string? value) return null; } + return UrlDecode(value.AsSpan(), encoding); + } + + internal static string UrlDecode(ReadOnlySpan value, Encoding encoding) + { + if (value.IsEmpty) + { + return string.Empty; + } + int count = value.Length; - UrlDecoder helper = new UrlDecoder(count, encoding); + UrlDecoder helper = count <= MaxStackAllocUrlLength + ? new UrlDecoder(stackalloc char[MaxStackAllocUrlLength], stackalloc byte[MaxStackAllocUrlLength], encoding) + : new UrlDecoder(new char[count], new byte[count], encoding); // go through the string's chars collapsing %XX and %uXXXX and // appending each char as char, with exception of %XX constructs @@ -386,71 +406,40 @@ internal static string JavaScriptStringEncode(string? value) } [return: NotNullIfNotNull(nameof(bytes))] - internal static byte[]? UrlEncode(byte[]? bytes, int offset, int count, bool alwaysCreateNewReturnValue) - { - byte[]? encoded = UrlEncode(bytes, offset, count); - - return (alwaysCreateNewReturnValue && (encoded != null) && (encoded == bytes)) - ? (byte[])encoded.Clone() - : encoded; - } - - [return: NotNullIfNotNull(nameof(bytes))] - private static byte[]? UrlEncode(byte[]? bytes, int offset, int count) + internal static byte[]? UrlEncode(byte[]? bytes, int offset, int count) { if (!ValidateUrlEncodingParameters(bytes, offset, count)) { return null; } - int cSpaces = 0; - int cUnsafe = 0; - - // count them first - for (int i = 0; i < count; i++) - { - char ch = (char)bytes[offset + i]; - - if (ch == ' ') - { - cSpaces++; - } - else if (!HttpEncoderUtility.IsUrlSafeChar(ch)) - { - cUnsafe++; - } - } + return UrlEncode(bytes.AsSpan(offset, count)); + } + private static byte[] UrlEncode(ReadOnlySpan bytes) + { // nothing to expand? - if (cSpaces == 0 && cUnsafe == 0) + if (!NeedsEncoding(bytes, out int cUnsafe)) { - // DevDiv 912606: respect "offset" and "count" - if (0 == offset && bytes.Length == count) - { - return bytes; - } - else - { - byte[] subarray = new byte[count]; - Buffer.BlockCopy(bytes, offset, subarray, 0, count); - return subarray; - } + return bytes.ToArray(); } + return UrlEncode(bytes, cUnsafe); + } + + private static byte[] UrlEncode(ReadOnlySpan bytes, int cUnsafe) + { // expand not 'safe' characters into %XX, spaces to +s - byte[] expandedBytes = new byte[count + cUnsafe * 2]; + byte[] expandedBytes = new byte[bytes.Length + cUnsafe * 2]; int pos = 0; - for (int i = 0; i < count; i++) + foreach (byte b in bytes) { - byte b = bytes[offset + i]; - char ch = (char)b; - - if (HttpEncoderUtility.IsUrlSafeChar(ch)) + if (s_urlSafeBytes.Contains(b)) { expandedBytes[pos++] = b; } - else if (ch == ' ') + else if (b == ' ') { expandedBytes[pos++] = (byte)'+'; } @@ -465,6 +454,43 @@ internal static string JavaScriptStringEncode(string? value) return expandedBytes; } + private static bool NeedsEncoding(ReadOnlySpan bytes, out int cUnsafe) + { + cUnsafe = 0; + + int i = bytes.IndexOfAnyExcept(s_urlSafeBytes); + if (i < 0) + { + return false; + } + + foreach (byte b in bytes.Slice(i)) + { + if (!s_urlSafeBytes.Contains(b) && b != ' ') + { + cUnsafe++; + } + } + + return true; + } + + internal static byte[] UrlEncode(string str, Encoding e) + { + if (e.GetMaxByteCount(str.Length) <= StackallocThreshold) + { + Span byteSpan = stackalloc byte[StackallocThreshold]; + int encodedBytes = e.GetBytes(str, byteSpan); + + return UrlEncode(byteSpan.Slice(0, encodedBytes)); + } + + byte[] bytes = e.GetBytes(str); + return NeedsEncoding(bytes, out int cUnsafe) + ? UrlEncode(bytes, cUnsafe) + : bytes; + } + // Helper to encode the non-ASCII url characters only private static string UrlEncodeNonAscii(string str, Encoding e) { @@ -535,7 +561,7 @@ private static byte[] UrlEncodeNonAscii(byte[] bytes, int offset, int count) if ((ch & 0xff80) == 0) { // 7 bit? - if (HttpEncoderUtility.IsUrlSafeChar(ch)) + if (s_urlSafeBytes.Contains((byte)ch)) { sb.Append(ch); } @@ -626,17 +652,15 @@ private static bool ValidateUrlEncodingParameters([NotNullWhen(true)] byte[]? by } // Internal class to facilitate URL decoding -- keeps char buffer and byte buffer, allows appending of either chars or bytes - private sealed class UrlDecoder + private ref struct UrlDecoder { - private readonly int _bufferSize; - // Accumulate characters in a special array private int _numChars; - private readonly char[] _charBuffer; + private readonly Span _charBuffer; // Accumulate bytes for decoding into characters in a special array private int _numBytes; - private byte[]? _byteBuffer; + private readonly Span _byteBuffer; // Encoding to convert chars to bytes private readonly Encoding _encoding; @@ -645,19 +669,17 @@ private void FlushBytes() { if (_numBytes > 0) { - Debug.Assert(_byteBuffer != null); - _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars); + Debug.Assert(!_byteBuffer.IsEmpty); + _numChars += _encoding.GetChars(_byteBuffer.Slice(0, _numBytes), _charBuffer.Slice(_numChars)); _numBytes = 0; } } - internal UrlDecoder(int bufferSize, Encoding encoding) + internal UrlDecoder(Span charBuffer, Span byteBuffer, Encoding encoding) { - _bufferSize = bufferSize; + _charBuffer = charBuffer; + _byteBuffer = byteBuffer; _encoding = encoding; - - _charBuffer = new char[bufferSize]; - // byte buffer created on demand } internal void AddChar(char ch) @@ -681,8 +703,6 @@ internal void AddByte(byte b) else */ { - _byteBuffer ??= new byte[_bufferSize]; - _byteBuffer[_numBytes++] = b; } } @@ -694,7 +714,7 @@ internal string GetString() FlushBytes(); } - return _numChars > 0 ? new string(_charBuffer, 0, _numChars) : ""; + return _charBuffer.Slice(0, _numChars).ToString(); } } } diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs index 5afc887a68f40..a2cb3b9afe5c7 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs @@ -8,29 +8,6 @@ namespace System.Web.Util { internal static class HttpEncoderUtility { - // Set of safe chars, from RFC 1738.4 minus '+' - public static bool IsUrlSafeChar(char ch) - { - if (char.IsAsciiLetterOrDigit(ch)) - { - return true; - } - - switch (ch) - { - case '-': - case '_': - case '.': - case '!': - case '*': - case '(': - case ')': - return true; - } - - return false; - } - // Helper to encode spaces only [return: NotNullIfNotNull(nameof(str))] internal static string? UrlEncodeSpaces(string? str) => str != null && str.Contains(' ') ? str.Replace(" ", "%20") : str; diff --git a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml index 935f4a69d115a..05336ecc0ae91 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml @@ -1,5 +1,5 @@  - + CP0014 @@ -469,6 +469,12 @@ net8.0/System.dll net9.0/System.dll + + CP0015 + T:System.Text.RegularExpressions.GeneratedRegexAttribute:[T:System.AttributeUsageAttribute] + net8.0/System.Text.RegularExpressions.dll + net9.0/System.Text.RegularExpressions.dll + CP0021 T:System.Diagnostics.Metrics.MeasurementCallback`1``0:struct @@ -529,4 +535,4 @@ net8.0/System.Text.Json.dll net9.0/System.Text.Json.dll - \ No newline at end of file + diff --git a/src/libraries/apicompat/ApiCompatBaseline.netstandard2.0.xml b/src/libraries/apicompat/ApiCompatBaseline.netstandard2.0.xml index 939342fce86a0..16b5aad84310f 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.netstandard2.0.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.netstandard2.0.xml @@ -1,5 +1,5 @@  - + CP0001 diff --git a/src/libraries/apicompat/ApiCompatBaseline.netstandard2.1.xml b/src/libraries/apicompat/ApiCompatBaseline.netstandard2.1.xml index f0ef02d93c1a1..999352135ae92 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.netstandard2.1.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.netstandard2.1.xml @@ -1,5 +1,5 @@  - + CP0014 diff --git a/src/libraries/native-binplace.proj b/src/libraries/native-binplace.proj index 960704fe30acf..4ffe653978344 100644 --- a/src/libraries/native-binplace.proj +++ b/src/libraries/native-binplace.proj @@ -13,10 +13,10 @@ - + - + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index b6b47e7409493..654827596930d 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -465,6 +465,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs index 4083d7378ac1a..13ba7291b73bf 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs @@ -347,7 +347,7 @@ internal void check_override() { foreach (MethodInfo m in override_methods) { - if (m.IsVirtual && !IsVirtual) + if (m.IsVirtual && !IsVirtual && !IsStatic && !ReflectedType!.IsInterface) throw new TypeLoadException(SR.Format(SR.TypeLoad_MethodOverrideNotVirtual, name, m)); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 9be5b3d817dce..016cc3e08568c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -153,9 +153,6 @@ public static unsafe IntPtr AllocateTypeAssociatedMemory(Type type, int size) [Intrinsic] internal static ref byte GetRawData(this object obj) => ref obj.GetRawData(); - [Intrinsic] - public static bool IsReferenceOrContainsReferences() => IsReferenceOrContainsReferences(); - [Intrinsic] internal static bool IsBitwiseEquatable() => IsBitwiseEquatable(); @@ -214,7 +211,7 @@ private static extern unsafe IntPtr GetSpanDataFrom( private static extern bool SufficientExecutionStack(); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void InternalBox(QCallTypeHandle type, ref byte target, ObjectHandleOnStack result); + private static extern object InternalBox(QCallTypeHandle type, ref byte target); /// /// Create a boxed object of the specified type from the data located at the target reference. @@ -255,8 +252,7 @@ private static extern unsafe IntPtr GetSpanDataFrom( if (rtType.IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); - object? result = null; - InternalBox(new QCallTypeHandle(ref rtType), ref target, ObjectHandleOnStack.Create(ref result)); + object? result = InternalBox(new QCallTypeHandle(ref rtType), ref target); return result; } diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index f289911d790f3..f946ce7bc09da 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -37,7 +37,7 @@ internal enum TypeNameFormatFlags FormatFullInst } - internal partial class RuntimeType + internal unsafe partial class RuntimeType { #region Definitions @@ -1607,7 +1607,7 @@ private void CreateInstanceCheckThis() return CreateInstanceMono(!publicOnly, wrapExceptions); } - // Specialized version of the above for Activator.CreateInstance() + // Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance() [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] internal object? CreateInstanceOfT() @@ -1615,6 +1615,43 @@ private void CreateInstanceCheckThis() return CreateInstanceMono(false, true); } + // Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance() + [DebuggerStepThroughAttribute] + [Diagnostics.DebuggerHidden] + internal void CallDefaultStructConstructor(ref byte value) + { + Debug.Assert(IsValueType); + + RuntimeConstructorInfo? ctor = GetDefaultConstructor(); + if (ctor == null) + { + return; + } + + if (!ctor.IsPublic) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); + } + + // Important: when using the interpreter, GetFunctionPointer is an intrinsic that + // returns a function descriptor suitable for casting to a managed function pointer. + // Other ways of obtaining a function pointer might not work. + IntPtr ptr = ctor.MethodHandle.GetFunctionPointer(); + delegate* valueCtor = (delegate*)ptr; + if (valueCtor == null) + { + throw new ExecutionEngineException(); + } + + try + { + valueCtor(ref value); + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } #endregion private TypeCache? cache; diff --git a/src/mono/browser/README.md b/src/mono/browser/README.md index ced0882ae8315..a306ddedf3935 100644 --- a/src/mono/browser/README.md +++ b/src/mono/browser/README.md @@ -157,7 +157,7 @@ To run a test with `FooBar` in the name: `make run-debugger-tests TEST_FILTER=FooBar` -(See https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=xunit for filter options) +(See https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests?pivots=xunit for filter options) Additional arguments for `dotnet test` can be passed via `MSBUILD_ARGS` or `TEST_ARGS`. For example `MSBUILD_ARGS="/p:WasmDebugLevel=5"`. Though only one of `TEST_ARGS`, or `TEST_FILTER` can be used at a time. @@ -189,7 +189,7 @@ Also check [bench](../sample/wasm/browser-bench/README.md) sample to measure mon The wasm templates, located in the `templates` directory, are templates for `dotnet new`, VS and VS for Mac. They are packaged and distributed as part of the `wasm-experimental` workload. We have 2 templates, `wasmbrowser` and `wasmconsole`, for browser and console WebAssembly applications. -For details about using `dotnet new` see the dotnet tool [documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new). +For details about using `dotnet new` see the dotnet tool [documentation](https://learn.microsoft.com/dotnet/core/tools/dotnet-new). To test changes in the templates, use `dotnet new install --force src/mono/wasm/templates/templates/browser`. diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index bd5a3b149716f..fc8708fed161d 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -127,6 +127,7 @@ <_WasmPThreadPoolInitialSize Condition="'$(_WasmPThreadPoolInitialSize)' == ''">-1 <_WasmPThreadPoolUnusedSize Condition="'$(_WasmPThreadPoolUnusedSize)' == ''">-1 + <_WasmIsPublishing Condition="'$(_WasmIsPublishing)' == '' and '$(_IsPublishing)' != ''">$(_IsPublishing) @@ -152,6 +153,7 @@ ExtraConfig="@(WasmExtraConfig)" NativeAssets="@(WasmNativeAsset)" DebugLevel="$(WasmDebugLevel)" + IsPublish="$(_WasmIsPublishing)" IncludeThreadsWorker="$(WasmEnableThreads)" PThreadPoolInitialSize="$(_WasmPThreadPoolInitialSize)" PThreadPoolUnusedSize="$(_WasmPThreadPoolUnusedSize)" diff --git a/src/mono/browser/runtime/diagnostics/index.ts b/src/mono/browser/runtime/diagnostics/index.ts index f8f93f6781a07..622a31f97fda3 100644 --- a/src/mono/browser/runtime/diagnostics/index.ts +++ b/src/mono/browser/runtime/diagnostics/index.ts @@ -87,7 +87,7 @@ function diagnostic_options_from_environment (): DiagnosticOptions | null { } /// Parse a DOTNET_DiagnosticPorts string and return a DiagnosticOptions object. -/// See https://docs.microsoft.com/en-us/dotnet/core/diagnostics/diagnostic-port#configure-additional-diagnostic-ports +/// See https://learn.microsoft.com/dotnet/core/diagnostics/diagnostic-port#configure-additional-diagnostic-ports function diagnostic_options_from_ports_spec (val: string): DiagnosticOptions | null { if (val === "") return null; diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts index 21f5fa2131a34..471dd011bf414 100644 --- a/src/mono/browser/runtime/run.ts +++ b/src/mono/browser/runtime/run.ts @@ -12,7 +12,7 @@ import { postCancelThreads, terminateAllThreads } from "./pthreads"; import { call_entry_point } from "./managed-exports"; /** - * Possible signatures are described here https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line + * Possible signatures are described here https://learn.microsoft.com/dotnet/csharp/fundamentals/program-structure/main-command-line */ export async function mono_run_main_and_exit (main_assembly_name?: string, args?: string[]): Promise { try { @@ -33,7 +33,7 @@ export async function mono_run_main_and_exit (main_assembly_name?: string, args? } /** - * Possible signatures are described here https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line + * Possible signatures are described here https://learn.microsoft.com/dotnet/csharp/fundamentals/program-structure/main-command-line */ export async function mono_run_main (main_assembly_name?: string, args?: string[]): Promise { if (main_assembly_name === undefined || main_assembly_name === null || main_assembly_name === "") { diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index ce43d282177f9..81ea280734fdf 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -3286,6 +3286,7 @@ dbg_path_get_basename (const char *filename) static GENERATE_TRY_GET_CLASS_WITH_CACHE (hidden_klass, "System.Diagnostics", "DebuggerHiddenAttribute") static GENERATE_TRY_GET_CLASS_WITH_CACHE (step_through_klass, "System.Diagnostics", "DebuggerStepThroughAttribute") static GENERATE_TRY_GET_CLASS_WITH_CACHE (non_user_klass, "System.Diagnostics", "DebuggerNonUserCodeAttribute") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (intptr_klass, "System", "IntPtr") static void init_jit_info_dbg_attrs (MonoJitInfo *ji) @@ -5578,6 +5579,7 @@ decode_value_compute_size (MonoType *t, int type, MonoDomain *domain, guint8 *bu int ret = 0; if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) && !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) && + !(t->type == MONO_TYPE_I && type == MONO_TYPE_I8) && !(type == VALUE_TYPE_ID_FIXED_ARRAY) && !(type == MDBGPROT_VALUE_TYPE_ID_NULL) && !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) && @@ -5640,7 +5642,13 @@ decode_value_compute_size (MonoType *t, int type, MonoDomain *domain, guint8 *bu case MONO_TYPE_I: case MONO_TYPE_U: /* We send these as vtypes, so we get them back as such */ - g_assert (type == MONO_TYPE_VALUETYPE); + if (type == MONO_TYPE_I8) + { + ret += sizeof(guint64); + goto end; + } + else + g_assert (type == MONO_TYPE_VALUETYPE); /* Fall through */ handle_vtype: case MONO_TYPE_VALUETYPE: @@ -5697,6 +5705,7 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) && !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) && + !(t->type == MONO_TYPE_I && type == MONO_TYPE_I8) && !(type == VALUE_TYPE_ID_FIXED_ARRAY) && !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) && !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8) && @@ -5769,8 +5778,31 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, break; case MONO_TYPE_I: case MONO_TYPE_U: - /* We send these as vtypes, so we get them back as such */ - g_assert (type == MONO_TYPE_VALUETYPE); + if (type == MONO_TYPE_I8) //sometimes we send as a PTR because it's a IntPtr then the debugger will send back as I8 + { + MonoClassField *f = NULL; + gpointer iter = NULL; + MonoClass *intptr_klass = mono_class_try_get_intptr_klass_class (); + while ((f = mono_class_get_fields_internal (intptr_klass, &iter))) { + if (G_UNLIKELY (!f->type)) { + ERROR_DECL(error); + mono_field_resolve_type (f, error); + mono_error_cleanup (error); + if (!f->type) + continue; + } + if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted (f)) + continue; + guint8 * fieldAddr = mono_vtype_get_field_addr (addr, f); + *(gint64*)fieldAddr = decode_long (buf, &buf, limit); + } + *endbuf = buf; + return ERR_NONE; + } + else /* We send these as vtypes, so we get them back as such */ + g_assert (type == MONO_TYPE_VALUETYPE); /* Fall through */ handle_vtype: case MONO_TYPE_VALUETYPE: diff --git a/src/mono/mono/eglib/gmisc-win32.c b/src/mono/mono/eglib/gmisc-win32.c index 0b2b9b30a785f..451b82a36aa75 100644 --- a/src/mono/mono/eglib/gmisc-win32.c +++ b/src/mono/mono/eglib/gmisc-win32.c @@ -135,7 +135,7 @@ g_get_current_dir (void) if (retval != 0) { // the size might be larger than MAX_PATH - // https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd + // https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd if (retval > buffer_size) { buffer_size = retval; buffer = g_realloc (buffer, buffer_size*sizeof(gunichar2)); diff --git a/src/mono/mono/metadata/external-only.c b/src/mono/mono/metadata/external-only.c index 369d6e2c84951..8528a1701f546 100644 --- a/src/mono/mono/metadata/external-only.c +++ b/src/mono/mono/metadata/external-only.c @@ -731,7 +731,7 @@ mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot) * * Unlike \c mono_method_get_unmanaged_thunk, minimal marshaling is done to the method parameters in * the wrapper. See - * https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0 + * https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0 * The method must be static and only use blittable argument types. There is no exception out-argument. * * diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 53c95c8245fae..ad7044a08439f 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -434,7 +434,7 @@ HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_Runt HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, gpointer, 3, (MonoClassField_ptr, MonoType_ptr, gpointer)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr)) -HANDLES(RUNH_8, "InternalBox", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox, void, 3, (MonoQCallTypeHandle, char_ref, MonoObjectHandleOnStack)) +HANDLES(RUNH_8, "InternalBox", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox, MonoObject, 2, (MonoQCallTypeHandle, char_ref)) HANDLES(RUNH_7, "InternalGetHashCode", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalGetHashCode, int, 1, (MonoObject)) HANDLES(RUNH_3a, "PrepareMethod", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod, void, 3, (MonoMethod_ptr, gpointer, int)) HANDLES(RUNH_4, "RunClassConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor, void, 1, (MonoType_ptr)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index a1ab947afd36c..be76808491569 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -1213,8 +1213,8 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod (MonoMeth // FIXME: Implement } -void -ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox (MonoQCallTypeHandle type_handle, char* data, MonoObjectHandleOnStack obj, MonoError *error) +MonoObjectHandle +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox (MonoQCallTypeHandle type_handle, char* data, MonoError *error) { MonoType *type = type_handle.type; MonoClass *klass = mono_class_from_mono_type_internal (type); @@ -1222,15 +1222,9 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox (MonoQCallT g_assert (m_class_is_valuetype (klass)); mono_class_init_checked (klass, error); - goto_if_nok (error, error_ret); - - MonoObject* raw_obj = mono_value_box_checked (klass, data, error); - goto_if_nok (error, error_ret); + return_val_if_nok (error, NULL_HANDLE); - HANDLE_ON_STACK_SET(obj, raw_obj); - return; -error_ret: - HANDLE_ON_STACK_SET (obj, NULL); + return mono_value_box_handle (klass, data, error); } gint32 diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 90076ba1994ef..3369d3ee8173c 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -6818,9 +6818,11 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout } // Non-value types are illegal at the interop boundary. - if (type->type == MONO_TYPE_GENERICINST && !mono_type_generic_inst_is_valuetype (type)) { - lowering.by_reference = TRUE; - return lowering; + if (type->type == MONO_TYPE_GENERICINST) { + if (!mono_type_generic_inst_is_valuetype (type)) { + lowering.by_reference = TRUE; + return lowering; + } } else if (type->type != MONO_TYPE_VALUETYPE && !mono_type_is_primitive(type)) { lowering.by_reference = TRUE; return lowering; diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 6adb70bfda4d2..1ab165b6f95cc 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -364,7 +364,7 @@ netcore_probe_for_module (MonoImage *image, const char *file_name, int flags, Mo mono_error_move (bad_image_error, error); } - // TODO: Pass remaining flags on to LoadLibraryEx on Windows where appropriate, see https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportsearchpath?view=netcore-3.1 + // TODO: Pass remaining flags on to LoadLibraryEx on Windows where appropriate, see https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportsearchpath?view=netcore-3.1 if (!module && !is_ok (bad_image_error)) { mono_error_cleanup (error); diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index daaba307f17db..e464c87b9e3b7 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -1448,7 +1448,7 @@ typedef enum { GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT = 0x0004, GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT = 0x0008, GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT = 0x0010, - GENERIC_PARAMETER_ATTRIBUTE_ACCEPT_BYREFLIKE_CONSTRAINTS = 0x0020, // type argument can be ByRefLike + GENERIC_PARAMETER_ATTRIBUTE_ALLOW_BYREFLIKE_CONSTRAINTS = 0x0020, // type argument can be ByRefLike GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK = 0x003c } GenericParameterAttributes; diff --git a/src/mono/mono/metadata/verify.c b/src/mono/mono/metadata/verify.c index 621599fa4eeeb..134a63a3bf2d5 100644 --- a/src/mono/mono/metadata/verify.c +++ b/src/mono/mono/metadata/verify.c @@ -87,7 +87,7 @@ is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *co return FALSE; } - if (m_class_is_byreflike (paramClass) && (param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_ACCEPT_BYREFLIKE_CONSTRAINTS) == 0) + if (m_class_is_byreflike (paramClass) && (param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_ALLOW_BYREFLIKE_CONSTRAINTS) == 0) return FALSE; if (!param_info->constraints && !(param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK)) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 57edfe8b0873a..119b945ee9fa1 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -5417,7 +5417,7 @@ add_native_to_managed_wrappers (MonoAotCompile *acfg) /* this cannot be enforced by the C# compiler so we must give the user some warning before aborting */ if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) { - g_warning ("AOT restriction: Method '%s' must be static since it is decorated with [MonoPInvokeCallback]. See https://docs.microsoft.com/xamarin/ios/internals/limitations#reverse-callbacks", + g_warning ("AOT restriction: Method '%s' must be static since it is decorated with [MonoPInvokeCallback]. See https://learn.microsoft.com/xamarin/ios/internals/limitations#reverse-callbacks", mono_method_full_name (method, TRUE)); exit (1); } @@ -5425,7 +5425,7 @@ add_native_to_managed_wrappers (MonoAotCompile *acfg) sig = mono_method_signature_internal (e->ctor); if (sig->param_count != 1 || !(sig->params [0]->type == MONO_TYPE_CLASS && !strcmp (m_class_get_name (mono_class_from_mono_type_internal (sig->params [0])), "Type"))) { - g_warning ("AOT restriction: The attribute [MonoPInvokeCallback] used on method '%s' must take a System.Type argument. See https://docs.microsoft.com/xamarin/ios/internals/limitations#reverse-callbacks", + g_warning ("AOT restriction: The attribute [MonoPInvokeCallback] used on method '%s' must take a System.Type argument. See https://learn.microsoft.com/xamarin/ios/internals/limitations#reverse-callbacks", mono_method_full_name (method, TRUE)); exit (1); } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 49bc8d1b45dd5..98bfc8cc02071 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2301,7 +2301,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas else if (MONO_TYPE_IS_PRIMITIVE (t)) has_refs = FALSE; else - has_refs = m_class_has_references (klass); + has_refs = m_class_has_references (klass) || m_class_has_ref_fields (klass); *op = has_refs ? MINT_LDC_I4_1 : MINT_LDC_I4_0; } else if (!strcmp (tm, "CreateSpan") && csignature->param_count == 1 && @@ -3325,11 +3325,11 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c { // P/Invoke calls shouldn't contain 'this' g_assert (!csignature->hasthis); - + /* - * Argument reordering here doesn't handle on the fly offset allocation + * Argument reordering here doesn't handle on the fly offset allocation * and requires the full var offset allocator pass that is only ran for optimized code - */ + */ g_assert (td->optimized); MonoMethodSignature *new_csignature; @@ -3337,7 +3337,7 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c StackInfo sp_fp = td->sp [-1]; --td->sp; - // Save the old arguments + // Save the old arguments td->sp -= csignature->param_count; StackInfo *sp_old_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); for (int i = 0; i < csignature->param_count; ++i) @@ -3349,7 +3349,7 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c MonoClass *swift_self = mono_class_try_get_swift_self_class (); MonoClass *swift_error = mono_class_try_get_swift_error_class (); /* - * Go through the lowered arguments, if the argument is a struct, + * Go through the lowered arguments, if the argument is a struct, * we need to replace it with a sequence of lowered arguments. * Also record the updated parameters for the new signature. */ @@ -3362,7 +3362,7 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c if (!lowered_swift_struct.by_reference) { for (uint32_t idx_lowered = 0; idx_lowered < lowered_swift_struct.num_lowered_elements; ++idx_lowered) { int mt_lowered = mono_mint_type (lowered_swift_struct.lowered_elements [idx_lowered]); - int lowered_elem_size = mono_type_size (lowered_swift_struct.lowered_elements [idx_lowered], &align); + int lowered_elem_size = mono_type_size (lowered_swift_struct.lowered_elements [idx_lowered], &align); // Load the lowered elements of the struct interp_add_ins (td, MINT_MOV_SRC_OFF); interp_ins_set_sreg (td->last_ins, sp_old_params [idx_param].var); @@ -3371,7 +3371,7 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c td->last_ins->data [2] = GINT_TO_UINT16 (lowered_elem_size); push_mono_type (td, lowered_swift_struct.lowered_elements [idx_lowered], mt_lowered, mono_class_from_mono_type_internal (lowered_swift_struct.lowered_elements [idx_lowered])); interp_ins_set_dreg (td->last_ins, td->sp [-1].var); - + ++new_param_count; g_array_append_val (new_params, lowered_swift_struct.lowered_elements [idx_lowered]); } diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 2c629adf59662..f4a92af12e386 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -992,7 +992,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign else if (cfg->gshared && (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (t)) EMIT_NEW_ICONST (cfg, ins, 1); else if (!cfg->gshared || !mini_class_check_context_used (cfg, klass)) - EMIT_NEW_ICONST (cfg, ins, m_class_has_references (klass) ? 1 : 0); + EMIT_NEW_ICONST (cfg, ins, m_class_has_references (klass) || m_class_has_ref_fields (klass) ? 1 : 0); else { g_assert (cfg->gshared); @@ -1066,7 +1066,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign const int element_size = mono_type_size (t, &alignment); const int num_elements = mono_type_size (field->type, &alignment) / element_size; const int obj_size = MONO_ABI_SIZEOF (MonoObject); - + MonoInst* span = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL); MonoInst* span_addr; EMIT_NEW_TEMPLOADA (cfg, span_addr, span->inst_c0); @@ -1082,7 +1082,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign const int swizzle = element_size; #endif gpointer data_ptr = (gpointer)mono_field_get_rva (field, swizzle); - EMIT_NEW_PCONST (cfg, ptr_inst, data_ptr); + EMIT_NEW_PCONST (cfg, ptr_inst, data_ptr); } MonoClassField* field_ref = mono_class_get_field_from_name_full (span->klass, "_reference", NULL); @@ -2135,8 +2135,8 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign return ins; /* Fallback if SIMD is disabled */ - if (in_corlib && - ((!strcmp ("System.Numerics", cmethod_klass_name_space) && !strcmp ("Vector", cmethod_klass_name)) || + if (in_corlib && + ((!strcmp ("System.Numerics", cmethod_klass_name_space) && !strcmp ("Vector", cmethod_klass_name)) || !strncmp ("System.Runtime.Intrinsics", cmethod_klass_name_space, 25))) { if (!strcmp (cmethod->name, "get_IsHardwareAccelerated")) { EMIT_NEW_ICONST (cfg, ins, 0); @@ -2146,7 +2146,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } // On FullAOT, return false for RuntimeFeature: - // - IsDynamicCodeCompiled + // - IsDynamicCodeCompiled // - IsDynamicCodeSupported and no interpreter // otherwise use the C# code in System.Private.CoreLib if (in_corlib && diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 35ae4b04161d6..932fcf010b1cb 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -2820,7 +2820,7 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_ if (mono_aot_only) { char *fullname = mono_method_get_full_name (method); - mono_error_set_execution_engine (error, "Attempting to JIT compile method '%s' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.\n", fullname); + mono_error_set_execution_engine (error, "Attempting to JIT compile method '%s' while running in aot-only mode. See https://learn.microsoft.com/xamarin/ios/internals/limitations for more information.\n", fullname); g_free (fullname); return NULL; diff --git a/src/mono/mono/mini/monovm.c b/src/mono/mono/mini/monovm.c index baa5e66b36f76..9d9b1ae399b02 100644 --- a/src/mono/mono/mini/monovm.c +++ b/src/mono/mono/mini/monovm.c @@ -45,7 +45,7 @@ static gboolean parse_trusted_platform_assemblies (const char *assemblies_paths) { // From - // https://docs.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting#step-3---prepare-runtime-properties + // https://learn.microsoft.com/dotnet/core/tutorials/netcore-hosting#step-3---prepare-runtime-properties // this is ';' separated on Windows and ':' separated elsewhere. char **parts = g_strsplit (assemblies_paths, G_SEARCHPATH_SEPARATOR_S, 0); int asm_count = 0; @@ -187,7 +187,7 @@ static gboolean parse_properties (int propertyCount, const char **propertyKeys, const char **propertyValues) { // A partial list of relevant properties is at: - // https://docs.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting#step-3---prepare-runtime-properties + // https://learn.microsoft.com/dotnet/core/tutorials/netcore-hosting#step-3---prepare-runtime-properties PInvokeOverrideFn override_fn = NULL; for (int i = 0; i < propertyCount; ++i) { diff --git a/src/mono/mono/tests/array-coop-1.cs b/src/mono/mono/tests/array-coop-1.cs index c0d3476b74f1a..a879673531d0d 100644 --- a/src/mono/mono/tests/array-coop-1.cs +++ b/src/mono/mono/tests/array-coop-1.cs @@ -22,7 +22,7 @@ to internal non-public functions. System.Array.GetGenericValueImpl => ves_icall_System_Array_GetGenericValueImpl See -https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo.makegenericmethod?view=netframework-4.7.1. +https://learn.microsoft.com/dotnet/api/system.reflection.methodinfo.makegenericmethod?view=netframework-4.7.1. https://msdn.microsoft.com/en-us/library/system.array.rank(v=vs.110).aspx https://stackoverflow.com/questions/1067312/how-to-use-methodinfo-invoke-to-set-property-value */ diff --git a/src/mono/mono/tests/coreclr-security.cs b/src/mono/mono/tests/coreclr-security.cs index 0c33e3d41ba81..585bee9f956b3 100644 --- a/src/mono/mono/tests/coreclr-security.cs +++ b/src/mono/mono/tests/coreclr-security.cs @@ -149,7 +149,7 @@ public override void CriticalMethod () public delegate Object InvokeDelegate (Object obj, Object[] parms); -// the 0.1% case from https://docs.microsoft.com/en-us/archive/blogs/shawnfa/silverlight-security-iii-inheritance +// the 0.1% case from https://learn.microsoft.com/archive/blogs/shawnfa/silverlight-security-iii-inheritance public class TransparentClassWithSafeCriticalDefaultConstructor { [SecuritySafeCritical] diff --git a/src/mono/mono/utils/mono-mmap-windows.c b/src/mono/mono/utils/mono-mmap-windows.c index 6ef8a4e882f1a..0d40c2f96c5f7 100644 --- a/src/mono/mono/utils/mono-mmap-windows.c +++ b/src/mono/mono/utils/mono-mmap-windows.c @@ -204,7 +204,7 @@ mono_file_map_error (size_t length, int flags, int fd, guint64 offset, void **re // been one DWORD in 32bit Windows, expanded to SIZE_T in 64bit Windows. // It is 64bits even on 32bit Windows to allow large files. // - // See https://docs.microsoft.com/en-us/windows/desktop/Memory/creating-a-file-mapping-object. + // See https://learn.microsoft.com/windows/desktop/Memory/creating-a-file-mapping-object. const guint64 mapping_length = offset + length; #if HAVE_API_SUPPORT_WIN32_FILE_MAPPING diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgproj index b0b05dbe56697..dcb5b6882e53f 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgproj @@ -19,6 +19,8 @@ + + diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/Microsoft.NET.Sdk.WebAssembly.Pack.pkgproj b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/Microsoft.NET.Sdk.WebAssembly.Pack.pkgproj index cf39fdee47e1f..eb8950d089ab5 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/Microsoft.NET.Sdk.WebAssembly.Pack.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/Microsoft.NET.Sdk.WebAssembly.Pack.pkgproj @@ -8,7 +8,7 @@ - + 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 d161db4b45c82..a00c5587ad9ee 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 @@ -410,6 +410,12 @@ Copyright (c) .NET Foundation. All rights reserved. + + + $(MSBuildThisFileDirectory)browser.runtimeconfig.template.json + + + diff --git a/src/mono/wasm/templates/templates/browser/runtimeconfig.template.json b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/browser.runtimeconfig.template.json similarity index 100% rename from src/mono/wasm/templates/templates/browser/runtimeconfig.template.json rename to src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/browser.runtimeconfig.template.json diff --git a/src/mono/wasi/Wasi.Build.Tests/BuildTestBase.cs b/src/mono/wasi/Wasi.Build.Tests/BuildTestBase.cs index 5cbdd8fe7d917..0511818c5e2f3 100644 --- a/src/mono/wasi/Wasi.Build.Tests/BuildTestBase.cs +++ b/src/mono/wasi/Wasi.Build.Tests/BuildTestBase.cs @@ -574,7 +574,7 @@ public static (int exitCode, string buildOutput) RunProcess(string path, // this will ensure that all the async event handling has completed // and should be called after process.WaitForExit(int) - // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit?view=net-5.0#System_Diagnostics_Process_WaitForExit_System_Int32_ + // https://learn.microsoft.com/dotnet/api/system.diagnostics.process.waitforexit?view=net-5.0#System_Diagnostics_Process_WaitForExit_System_Int32_ process.WaitForExit(); process.ErrorDataReceived -= logStdErr; diff --git a/src/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets index cad25a7606729..27a3632bb3a60 100644 --- a/src/mono/wasi/build/WasiApp.targets +++ b/src/mono/wasi/build/WasiApp.targets @@ -455,5 +455,11 @@ wasi.start(instance); + + + $(MSBuildThisFileDirectory)wasiconsole.runtimeconfig.template.json + + + \ No newline at end of file diff --git a/src/mono/wasm/templates/templates/wasi-console/runtimeconfig.template.json b/src/mono/wasi/build/wasiconsole.runtimeconfig.template.json similarity index 100% rename from src/mono/wasm/templates/templates/wasi-console/runtimeconfig.template.json rename to src/mono/wasi/build/wasiconsole.runtimeconfig.template.json diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs index 03db9aed60118..d20a8cc269579 100644 --- a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs @@ -517,7 +517,7 @@ public static (int exitCode, string buildOutput) RunProcess(string path, // this will ensure that all the async event handling has completed // and should be called after process.WaitForExit(int) - // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit?view=net-5.0#System_Diagnostics_Process_WaitForExit_System_Int32_ + // https://learn.microsoft.com/dotnet/api/system.diagnostics.process.waitforexit?view=net-5.0#System_Diagnostics_Process_WaitForExit_System_Int32_ process.WaitForExit(); process.ErrorDataReceived -= logStdErr; diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTests.cs deleted file mode 100644 index ee69b34819bbd..0000000000000 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTests.cs +++ /dev/null @@ -1,109 +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.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Xunit; -using Xunit.Abstractions; - -#nullable enable - -namespace Wasm.Build.Tests.TestAppScenarios; - -public class DebugLevelTests : AppTestBase -{ - public DebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) - : base(output, buildContext) - { - } - - private void AssertDebugLevel(RunResult result, int value) - { - Assert.Collection( - result.TestOutput, - m => Assert.Equal($"WasmDebugLevel: {value}", m) - ); - } - - [Theory] - [InlineData("Debug")] - [InlineData("Release")] - public async Task BuildWithDefaultLevel(string configuration) - { - CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithDefaultLevel_{configuration}", "App"); - BuildProject(configuration); - - var result = await RunSdkStyleAppForBuild(new( - Configuration: configuration, - TestScenario: "DebugLevelTest" - )); - AssertDebugLevel(result, -1); - } - - [Theory] - [InlineData("Debug", 1)] - [InlineData("Release", 1)] - [InlineData("Debug", 0)] - [InlineData("Release", 0)] - public async Task BuildWithExplicitValue(string configuration, int debugLevel) - { - CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithExplicitValue_{configuration}", "App"); - BuildProject(configuration: configuration, extraArgs: $"-p:WasmDebugLevel={debugLevel}"); - - var result = await RunSdkStyleAppForBuild(new( - Configuration: configuration, - TestScenario: "DebugLevelTest" - )); - AssertDebugLevel(result, debugLevel); - } - - [Theory] - [InlineData("Debug")] - [InlineData("Release")] - public async Task PublishWithDefaultLevel(string configuration) - { - CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevel_{configuration}", "App"); - PublishProject(configuration); - - var result = await RunSdkStyleAppForPublish(new( - Configuration: configuration, - TestScenario: "DebugLevelTest" - )); - AssertDebugLevel(result, 0); - } - - [Theory] - [InlineData("Debug", 1)] - [InlineData("Release", 1)] - [InlineData("Debug", -1)] - [InlineData("Release", -1)] - public async Task PublishWithExplicitValue(string configuration, int debugLevel) - { - CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithExplicitValue_{configuration}", "App"); - PublishProject(configuration, RuntimeVariant.SingleThreaded, assertAppBundle: true, $"-p:WasmDebugLevel={debugLevel}"); - - var result = await RunSdkStyleAppForPublish(new( - Configuration: configuration, - TestScenario: "DebugLevelTest" - )); - AssertDebugLevel(result, debugLevel); - } - - [Theory] - [InlineData("Debug")] - [InlineData("Release")] - public async Task PublishWithDefaultLevelAndPdbs(string configuration) - { - CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}", "App"); - PublishProject(configuration, RuntimeVariant.SingleThreaded, assertAppBundle: true, $"-p:CopyOutputSymbolsToPublishDirectory=true"); - - var result = await RunSdkStyleAppForPublish(new( - Configuration: configuration, - TestScenario: "DebugLevelTest" - )); - AssertDebugLevel(result, -1); - } -} diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs new file mode 100644 index 0000000000000..cac95e26fa3f2 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public abstract class DebugLevelTestsBase : AppTestBase +{ + public DebugLevelTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected void AssertDebugLevel(RunResult result, int value) + { + Assert.Collection( + result.TestOutput, + m => Assert.Equal($"WasmDebugLevel: {value}", m) + ); + } + + protected abstract void SetupProject(string projectId); + protected abstract Task RunForBuild(string configuration); + protected abstract Task RunForPublish(string configuration); + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task BuildWithDefaultLevel(string configuration) + { + SetupProject($"DebugLevelTests_BuildWithDefaultLevel_{configuration}"); + BuildProject(configuration, assertAppBundle: false); + + var result = await RunForBuild(configuration); + AssertDebugLevel(result, -1); + } + + [Theory] + [InlineData("Debug", 1)] + [InlineData("Release", 1)] + [InlineData("Debug", 0)] + [InlineData("Release", 0)] + public async Task BuildWithExplicitValue(string configuration, int debugLevel) + { + SetupProject($"DebugLevelTests_BuildWithExplicitValue_{configuration}"); + BuildProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}"); + + var result = await RunForBuild(configuration); + AssertDebugLevel(result, debugLevel); + } + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task PublishWithDefaultLevel(string configuration) + { + SetupProject($"DebugLevelTests_PublishWithDefaultLevel_{configuration}"); + PublishProject(configuration, assertAppBundle: false); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, 0); + } + + [Theory] + [InlineData("Debug", 1)] + [InlineData("Release", 1)] + [InlineData("Debug", -1)] + [InlineData("Release", -1)] + public async Task PublishWithExplicitValue(string configuration, int debugLevel) + { + SetupProject($"DebugLevelTests_PublishWithExplicitValue_{configuration}"); + PublishProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}"); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, debugLevel); + } +} diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs new file mode 100644 index 0000000000000..83809b3c2a9a3 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public class WasmAppBuilderDebugLevelTests : DebugLevelTestsBase +{ + public WasmAppBuilderDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected override void SetupProject(string projectId) + { + Id = $"{projectId}_{GetRandomId()}"; + string projectfile = CreateWasmTemplateProject(Id, "wasmconsole"); + string projectDir = Path.GetDirectoryName(projectfile)!; + string mainJs = Path.Combine(projectDir, "main.mjs"); + string mainJsContent = File.ReadAllText(mainJs); + mainJsContent = mainJsContent + .Replace("import { dotnet }", "import { dotnet, exit }") + .Replace("await runMainAndExit()", "console.log('TestOutput -> WasmDebugLevel: ' + config.debugLevel); exit(0)"); + File.WriteAllText(mainJs, mainJsContent); + } + + protected override Task RunForBuild(string configuration) + { + CommandResult res = new RunCommand(s_buildEnv, _testOutput) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}"); + + return Task.FromResult(ProcessRunOutput(res)); + } + + private RunResult ProcessRunOutput(CommandResult res) + { + var output = res.Output.Split(Environment.NewLine); + _testOutput.WriteLine($"DEBUG: parsed lines '{String.Join(", ", output)}'"); + + var prefix = "[] TestOutput -> "; + var testOutput = output + .Where(l => l.StartsWith(prefix)) + .Select(l => l.Substring(prefix.Length)) + .ToArray(); + + _testOutput.WriteLine($"DEBUG: testOutput '{String.Join(", ", testOutput)}'"); + return new RunResult(res.ExitCode, testOutput, output, []); + } + + protected override Task RunForPublish(string configuration) + { + // WasmAppBuilder does publish to the same folder as build (it overrides the output), + // and thus using dotnet run work correctly for publish as well. + CommandResult res = new RunCommand(s_buildEnv, _testOutput) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}"); + + return Task.FromResult(ProcessRunOutput(res)); + } +} diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs new file mode 100644 index 0000000000000..b3d1a0316c49e --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public class WasmSdkDebugLevelTests : DebugLevelTestsBase +{ + public WasmSdkDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected override void SetupProject(string projectId) => CopyTestAsset("WasmBasicTestApp", projectId, "App"); + + protected override Task RunForBuild(string configuration) => RunSdkStyleAppForBuild(new( + Configuration: configuration, + TestScenario: "DebugLevelTest" + )); + + protected override Task RunForPublish(string configuration) => RunSdkStyleAppForPublish(new( + Configuration: configuration, + TestScenario: "DebugLevelTest" + )); + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task PublishWithDefaultLevelAndPdbs(string configuration) + { + SetupProject($"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}"); + PublishProject(configuration, assertAppBundle: false, extraArgs: $"-p:CopyOutputSymbolsToPublishDirectory=true"); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, -1); + } +} diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index 54a202840b41f..98ef4fcd61bdc 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -446,7 +446,7 @@ + Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=;_WasmIsPublishing=$(_IsPublishing)"> diff --git a/src/mono/wasm/features.md b/src/mono/wasm/features.md index 53995e656a1a6..627c87dfd76aa 100644 --- a/src/mono/wasm/features.md +++ b/src/mono/wasm/features.md @@ -53,7 +53,7 @@ Older versions of NodeJS hosts may need `--experimental-wasm-eh` command line op Passing Int64 and UInt64 values between JavaScript and C# requires support for the JavaScript `BigInt` type. See [JS-BigInt](https://github.com/WebAssembly/JS-BigInt-integration) for more information on this API. ### fetch - HTTP client -If an application uses the [HttpClient](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) managed API, your web browser must support the [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API for it to run. +If an application uses the [HttpClient](https://learn.microsoft.com/dotnet/api/system.net.http.httpclient) managed API, your web browser must support the [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API for it to run. Because web browsers do not expose direct access to sockets, we are unable to provide our own implementation of HTTP, and HttpClient's behavior and feature set will as a result depend also on the browser you use to run the application. @@ -62,7 +62,7 @@ A prominent limitation is that your application must obey `Cross-Origin Resource For your application to be able to perform HTTP requests in a NodeJS host, you need to install the `node-fetch` and `node-abort-controller` npm packages. ### WebSocket -Applications using the [WebSocketClient](https://learn.microsoft.com/en-us/dotnet/api/system.net.websockets.clientwebsocket) managed API will require the browser to support the [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) API. +Applications using the [WebSocketClient](https://learn.microsoft.com/dotnet/api/system.net.websockets.clientwebsocket) managed API will require the browser to support the [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) API. As with HTTP and HttpClient, we are unable to ship a custom implementation of this feature, so its behavior will depend on the browser being used to run the application. @@ -90,7 +90,7 @@ Trimming will remove unused code from your application, which reduces applicatio Some applications will break if trimming is used without further configuration due to the trimmer not knowing which code is used, for example any code accessed via reflection or serialization or dependency injection. -One typical source of trimming issues is JSON serialization/deserialization. The solution is to use [Source Generation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation), as shown below: +One typical source of trimming issues is JSON serialization/deserialization. The solution is to use [Source Generation](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/source-generation), as shown below: ```csharp [JsonSerializable(typeof(List))] @@ -99,7 +99,7 @@ partial class ItemListSerializerContext : JsonSerializerContext { } var json = JsonSerializer.Serialize(items, ItemListSerializerContext.Default.ListItem); ``` -Please ensure that you have thoroughly tested your application with trimming enabled before deployment, as the issues it causes may only appear in obscure parts of your software. For more advice on how to use trimming, see [trimming guidance](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained). +Please ensure that you have thoroughly tested your application with trimming enabled before deployment, as the issues it causes may only appear in obscure parts of your software. For more advice on how to use trimming, see [trimming guidance](https://learn.microsoft.com/dotnet/core/deploying/trimming/trim-self-contained). ### C code or native linked libraries Native rebuild will cause the .NET runtime to be re-built alongside your application, which allows you to link additional libraries into the WASM binary or change compiler configuration flags. @@ -354,7 +354,7 @@ It includes the WASM templates for `dotnet new` and also preview version of mult You can use browser dev tools to debug the JavaScript of the application and the runtime. You could also debug the C# code using our integration with browser dev tools or Visual Studio. -See detailed [documentation](https://learn.microsoft.com/en-us/aspnet/core/blazor/debug) +See detailed [documentation](https://learn.microsoft.com/aspnet/core/blazor/debug) You could also use it to debug the WASM code. In order to see `C` function names and debug symbols DWARF, see [Debug symbols](#Native-debug-symbols) @@ -420,7 +420,7 @@ await dotnet.withConfig({browserProfilerOptions: {}}).run(); ### Diagnostic tools -We have initial implementation of diagnostic server and [event pipe](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe) +We have initial implementation of diagnostic server and [event pipe](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe) At the moment it requires multi-threaded build of the runtime. diff --git a/src/native/corehost/CMakeLists.txt b/src/native/corehost/CMakeLists.txt index 358b885724507..a7b2c82de668d 100644 --- a/src/native/corehost/CMakeLists.txt +++ b/src/native/corehost/CMakeLists.txt @@ -4,6 +4,8 @@ project(corehost) include(../../../eng/native/configurepaths.cmake) include(${CLR_ENG_NATIVE_DIR}/configurecompiler.cmake) +set(COREHOST_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + if (MSVC) # Host components don't try to handle asynchronous exceptions set_property(DIRECTORY PROPERTY CLR_EH_OPTION /EHsc) @@ -19,7 +21,70 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU) add_compile_options($<$:-fno-use-cxa-atexit>) endif() +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CLR_ARTIFACTS_OBJ_DIR}) # Generated version files + +if (NOT ${CLR_SINGLE_FILE_HOST_ONLY}) + if("${CLI_CMAKE_PKG_RID}" STREQUAL "") + message(FATAL_ERROR "A minimum supported package rid is not specified (ex: win7-x86 or ubuntu.14.04-x64, osx.10.12-x64, rhel.7-x64)") + endif() + if("${CLI_CMAKE_COMMIT_HASH}" STREQUAL "") + message(FATAL_ERROR "Commit hash needs to be specified to build the host") + endif() +endif() + +if("${CLI_CMAKE_FALLBACK_OS}" STREQUAL "") + message(FATAL_ERROR "Fallback rid needs to be specified to build the host") +endif() + +if("${CLI_CMAKE_FALLBACK_OS}" STREQUAL "${CLR_CMAKE_TARGET_OS}") + add_compile_definitions(FALLBACK_OS_IS_SAME_AS_TARGET_OS) +endif() + +# Find support libraries that we need if they're present on the system. +find_library(PTHREAD_LIB pthread) + +# Prefer libatomic.a over libatomic.so. +set(_current_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) +list(PREPEND CMAKE_FIND_LIBRARY_SUFFIXES .a) +find_library(ATOMIC_SUPPORT_LIB atomic) +set(CMAKE_FIND_LIBRARY_SUFFIXES ${_current_CMAKE_FIND_LIBRARY_SUFFIXES}) +unset(_current_CMAKE_FIND_LIBRARY_SUFFIXES) + +configure_file(configure.h.in ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +# add_version_info_to_target(targetName [resourceDirName]) +function(add_version_info_to_target targetName) + set(RESOURCE_INCLUDE_DIR ${targetName}) + if (${ARGC} GREATER 1) + set(RESOURCE_INCLUDE_DIR ${ARGV1}) + endif() + + if (CLR_CMAKE_TARGET_WIN32) + target_sources(${targetName} PRIVATE ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/native.rc) + set_property(SOURCE ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/native.rc + TARGET_DIRECTORY ${targetName} + APPEND PROPERTY INCLUDE_DIRECTORIES + ${CLI_CMAKE_RESOURCE_DIR}/${RESOURCE_INCLUDE_DIR}) + else() + target_sources(${targetName} PRIVATE ${VERSION_FILE_PATH}) + endif() +endfunction() + +# This is required to map a symbol reference to a matching definition local to the module (.so) +# containing the reference instead of using definitions from other modules. +if(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_SUNOS) + add_link_options(LINKER:-Bsymbolic) +endif() + +add_library(fxr_resolver INTERFACE) +target_sources(fxr_resolver INTERFACE fxr_resolver.cpp) +target_include_directories(fxr_resolver INTERFACE fxr) + add_subdirectory(hostcommon) +add_subdirectory(hostmisc) add_subdirectory(fxr) add_subdirectory(hostpolicy) diff --git a/src/native/corehost/apphost/apphost.windows.cpp b/src/native/corehost/apphost/apphost.windows.cpp index b8167cf6c0d89..292652b8d9968 100644 --- a/src/native/corehost/apphost/apphost.windows.cpp +++ b/src/native/corehost/apphost/apphost.windows.cpp @@ -28,7 +28,7 @@ namespace HMODULE module = ::GetModuleHandleW(nullptr); assert(module != nullptr); - // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format + // https://learn.microsoft.com/windows/win32/debug/pe-format BYTE *bytes = reinterpret_cast(module); UINT32 pe_header_offset = reinterpret_cast(bytes)->e_lfanew; UINT16 subsystem = reinterpret_cast(bytes + pe_header_offset)->OptionalHeader.Subsystem; diff --git a/src/native/corehost/apphost/standalone/CMakeLists.txt b/src/native/corehost/apphost/standalone/CMakeLists.txt index 8a6a23934d85d..2630aed0deca3 100644 --- a/src/native/corehost/apphost/standalone/CMakeLists.txt +++ b/src/native/corehost/apphost/standalone/CMakeLists.txt @@ -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. -project(apphost) -set(DOTNET_PROJECT_NAME "apphost") - # Add RPATH to the apphost binary that allows using local copies of shared libraries # dotnet core depends on for special scenarios when system wide installation of such # dependencies is not possible for some reason. @@ -14,13 +11,12 @@ if (NOT CLR_CMAKE_TARGET_OSX) set(CMAKE_INSTALL_RPATH "\$ORIGIN/netcoredeps") endif() -set(SKIP_VERSIONING 1) - include_directories(..) set(SOURCES ../bundle_marker.cpp ./hostfxr_resolver.cpp + ../../corehost.cpp ) set(HEADERS @@ -29,7 +25,7 @@ set(HEADERS ) if(CLR_CMAKE_TARGET_WIN32) - add_definitions(-DUNICODE) + add_compile_definitions(UNICODE) list(APPEND SOURCES ../apphost.windows.cpp) @@ -37,20 +33,31 @@ if(CLR_CMAKE_TARGET_WIN32) ../apphost.windows.h) endif() -include(../../exe.cmake) +if(CLR_CMAKE_TARGET_WIN32) + list(APPEND SOURCES ${HEADERS}) +endif() + +add_compile_definitions(FEATURE_APPHOST) + +add_executable(apphost ${SOURCES} ${RESOURCES}) + +target_link_libraries(apphost PRIVATE hostmisc fxr_resolver) + +add_sanitizer_runtime_support(apphost) + +if(NOT CLR_CMAKE_TARGET_WIN32) + disable_pax_mprotect(apphost) +endif() -add_definitions(-DFEATURE_APPHOST=1) +install_with_stripped_symbols(apphost TARGETS corehost) # Disable manifest generation into the file .exe on Windows if(CLR_CMAKE_TARGET_WIN32) - set_property(TARGET ${PROJECT_NAME} PROPERTY - LINK_FLAGS "/MANIFEST:NO" - ) + target_link_options(apphost PRIVATE "/MANIFEST:NO") endif() -# Specify non-default Windows libs to be used for Arm64 builds -if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64) - target_link_libraries(apphost PRIVATE shell32.lib) +if (CLR_CMAKE_TARGET_WIN32) + target_link_libraries(apphost PRIVATE shell32) endif() if (CLR_CMAKE_HOST_APPLE) diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index 7a14c7c92ed66..e431b87e33d0c 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -14,29 +14,35 @@ if (NOT CLR_CMAKE_TARGET_APPLE) set(CMAKE_INSTALL_RPATH "\$ORIGIN/netcoredeps") endif() -set(SKIP_VERSIONING 1) - include_directories(..) +include_directories(../..) +include_directories(../../hostmisc) include_directories(../../json) include_directories(${CLR_SRC_NATIVE_DIR}/libs/System.IO.Compression.Native) include_directories(${CLR_SRC_NATIVE_DIR}/libs/Common) +include_directories(${CLR_ARTIFACTS_OBJ_DIR}) # Generated version files + +add_subdirectory(../../hostmisc hostmisc) + +configure_file(${CLR_SRC_NATIVE_DIR}/corehost/configure.h.in ${GENERATED_INCLUDE_DIR}/corehost/configure.h) +target_include_directories(hostmisc PUBLIC ${GENERATED_INCLUDE_DIR}/corehost) set(SOURCES ../bundle_marker.cpp ./hostfxr_resolver.cpp ./hostpolicy_resolver.cpp ../../hostpolicy/static/coreclr_resolver.cpp + ../../fxr_resolver.cpp + ../../corehost.cpp ) set(HEADERS ../bundle_marker.h ../../hostfxr_resolver.h + ../../fxr_resolver.h ) -add_definitions(-D_NO_ASYNCRTIMP) -add_definitions(-D_NO_PPLXIMP) -remove_definitions(-DEXPORT_SHARED_API) -add_definitions(-DNATIVE_LIBS_EMBEDDED) +add_compile_definitions(NATIVE_LIBS_EMBEDDED) include(../../fxr/files.cmake) include(../../hostpolicy/files.cmake) @@ -45,6 +51,7 @@ include(../../hostcommon/files.cmake) if(MSVC) # Host components don't try to handle asynchronous exceptions set_property(DIRECTORY PROPERTY CLR_EH_OPTION /EHsc) + set_property(TARGET hostmisc PROPERTY CLR_EH_OPTION /EHsc) elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU) # Prevents libc from calling pthread_cond_destroy on static objects in # dlopen()'ed library which we dlclose() in pal::unload_library. @@ -52,7 +59,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU) endif() if(CLR_CMAKE_TARGET_WIN32) - add_definitions(-DUNICODE) + add_compile_definitions(UNICODE) list(APPEND SOURCES ../apphost.windows.cpp ${CLR_SRC_NATIVE_DIR}/libs/Common/delayloadhook_windows.cpp @@ -78,11 +85,16 @@ else() set_exports_linker_option(${EXPORTS_FILE}) endif() -if (CLR_SINGLE_FILE_HOST_ONLY) - set(ADDITIONAL_INSTALL_ARGUMENTS COMPONENT runtime) +add_executable(singlefilehost ${SOURCES}) + +add_sanitizer_runtime_support(singlefilehost) + +if(NOT CLR_CMAKE_TARGET_WIN32) + disable_pax_mprotect(singlefilehost) endif() -include(../../exe.cmake) +install_with_stripped_symbols(singlefilehost TARGETS corehost COMPONENT runtime) + include(configure.cmake) if(CLR_CMAKE_HOST_UNIX) @@ -262,4 +274,6 @@ target_link_libraries( ${END_WHOLE_ARCHIVE} ) +target_link_libraries(singlefilehost PRIVATE hostmisc) + add_sanitizer_runtime_support(singlefilehost) diff --git a/src/native/corehost/comhost/CMakeLists.txt b/src/native/corehost/comhost/CMakeLists.txt index 9f777bff56997..0e7a045146bde 100644 --- a/src/native/corehost/comhost/CMakeLists.txt +++ b/src/native/corehost/comhost/CMakeLists.txt @@ -1,18 +1,12 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(comhost) - -set(DOTNET_PROJECT_NAME "comhost") - # Include directories -include_directories(../fxr) include_directories(../json) # CMake does not recommend using globbing since it messes with the freshness checks set(SOURCES comhost.cpp - ../fxr_resolver.cpp clsidmap.cpp ../redirected_error_writer.cpp ) @@ -26,9 +20,12 @@ if(CLR_CMAKE_TARGET_WIN32) Exports.def) endif() -include(../lib.cmake) +add_compile_definitions(FEATURE_LIBHOST) +add_compile_definitions(EXPORT_SHARED_API) + +add_library(comhost SHARED ${SOURCES}) -add_definitions(-DFEATURE_LIBHOST=1) +add_version_info_to_target(comhost) if (CLR_CMAKE_TARGET_WIN32) set(WINLIBS wintrust.lib) @@ -42,4 +39,4 @@ if (CLR_CMAKE_TARGET_WIN32) endif() install_with_stripped_symbols(comhost TARGETS corehost) -target_link_libraries(comhost PRIVATE libhostcommon) +target_link_libraries(comhost PRIVATE libhostcommon fxr_resolver) diff --git a/src/native/corehost/comhost/clsidmap.cpp b/src/native/corehost/comhost/clsidmap.cpp index 978cdeddb104b..001649765f662 100644 --- a/src/native/corehost/comhost/clsidmap.cpp +++ b/src/native/corehost/comhost/clsidmap.cpp @@ -115,14 +115,14 @@ namespace GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2; // File from disk must be used since there is no support for blob verification of a DLL - // https://docs.microsoft.com/windows/desktop/api/wintrust/ns-wintrust-wintrust_file_info + // https://learn.microsoft.com/windows/desktop/api/wintrust/ns-wintrust-wintrust_file_info WINTRUST_FILE_INFO fileData{}; fileData.cbStruct = sizeof(WINTRUST_FILE_INFO); fileData.pcwszFilePath = path.c_str(); fileData.hFile = nullptr; fileData.pgKnownSubject = nullptr; - // https://docs.microsoft.com/windows/desktop/api/wintrust/ns-wintrust-_wintrust_data + // https://learn.microsoft.com/windows/desktop/api/wintrust/ns-wintrust-_wintrust_data WINTRUST_DATA trustData{}; trustData.cbStruct = sizeof(trustData); trustData.pPolicyCallbackData = nullptr; @@ -137,7 +137,7 @@ namespace trustData.dwUIContext = 0; trustData.pFile = &fileData; - // https://docs.microsoft.com/windows/desktop/api/wintrust/nf-wintrust-winverifytrust + // https://learn.microsoft.com/windows/desktop/api/wintrust/nf-wintrust-winverifytrust LONG res = ::WinVerifyTrust(nullptr, &policy, &trustData); const DWORD err = ::GetLastError(); if (trustData.hWVTStateData != nullptr) diff --git a/src/native/corehost/common.cmake b/src/native/corehost/common.cmake deleted file mode 100644 index 9a5e3a6686e07..0000000000000 --- a/src/native/corehost/common.cmake +++ /dev/null @@ -1,58 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(${DOTNET_PROJECT_NAME}) - -include(${CMAKE_CURRENT_LIST_DIR}/setup.cmake) - -# Include directories -if(CLR_CMAKE_TARGET_WIN32) - include_directories("${CLI_CMAKE_RESOURCE_DIR}/${DOTNET_PROJECT_NAME}") -endif() -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) -include_directories(${CMAKE_CURRENT_LIST_DIR}/) -include_directories(${CMAKE_CURRENT_LIST_DIR}/../) -include_directories(${CMAKE_CURRENT_LIST_DIR}/hostmisc) -include_directories(${CLR_ARTIFACTS_OBJ_DIR}) # Generated version files - -set(RESOURCES) -if (CLR_CMAKE_TARGET_WIN32) - if (NOT SKIP_VERSIONING) - list(APPEND RESOURCES ${CMAKE_CURRENT_LIST_DIR}/native.rc) - endif() -else() - list(APPEND SOURCES ${VERSION_FILE_PATH}) -endif() - -if(CLR_CMAKE_TARGET_WIN32) - list(APPEND SOURCES ${HEADERS}) -endif() - -# This is required to map a symbol reference to a matching definition local to the module (.so) -# containing the reference instead of using definitions from other modules. -if(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_SUNOS) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker -Bsymbolic") -endif() - -function(set_common_libs TargetType) - - # Libraries used for exe projects - if (${TargetType} STREQUAL "exe") - if((CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_FREEBSD) AND NOT CLR_CMAKE_TARGET_ANDROID) - target_link_libraries (${DOTNET_PROJECT_NAME} PRIVATE "pthread") - endif() - endif() - - if (NOT ${TargetType} STREQUAL "lib-static") - # Specify the import library to link against for Arm32 build since the default set is minimal - if (CLR_CMAKE_TARGET_ARCH_ARM) - if (CLR_CMAKE_TARGET_WIN32) - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE shell32.lib advapi32.lib) - else() - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE atomic.a) - endif() - endif() - - target_link_libraries (${DOTNET_PROJECT_NAME} PRIVATE ${CMAKE_DL_LIBS}) - endif() -endfunction() diff --git a/src/native/corehost/configure.h.in b/src/native/corehost/configure.h.in new file mode 100644 index 0000000000000..4ddf2e6cfa7b0 --- /dev/null +++ b/src/native/corehost/configure.h.in @@ -0,0 +1,23 @@ +#ifndef PAL_HOST_CONFIGURE_H_INCLUDED +#define PAL_HOST_CONFIGURE_H_INCLUDED + +#cmakedefine01 CLR_SINGLE_FILE_HOST_ONLY + +#ifdef CLR_SINGLE_FILE_HOST_ONLY +// When hosting components are all statically linked, +// the versioning information is irrelevant and may only come up in tracing. +// so we will use "static" +#define HOST_POLICY_PKG_NAME "static" +#define HOST_POLICY_PKG_REL_DIR "static" +#define REPO_COMMIT_HASH "static" +#else +#define HOST_POLICY_PKG_NAME "runtime.@CLI_CMAKE_PKG_RID@.Microsoft.NETCore.DotNetHostPolicy" +#define HOST_POLICY_PKG_REL_DIR "runtime.@CLI_CMAKE_PKG_RID@/native" +#define REPO_COMMIT_HASH "@CLI_CMAKE_COMMIT_HASH@" +#endif + +#define FALLBACK_HOST_OS "@CLI_CMAKE_FALLBACK_OS@" +#define CURRENT_OS_NAME "@CLR_CMAKE_TARGET_OS@" +#define CURRENT_ARCH_NAME "@CLR_CMAKE_TARGET_ARCH@" + +#endif // PAL_HOST_CONFIGURE_H_INCLUDED \ No newline at end of file diff --git a/src/native/corehost/corehost.cpp b/src/native/corehost/corehost.cpp index 5edc2fbf5d521..902f8acace131 100644 --- a/src/native/corehost/corehost.cpp +++ b/src/native/corehost/corehost.cpp @@ -99,7 +99,10 @@ void need_newer_framework_error(const pal::string_t& dotnet_root, const pal::str int exe_start(const int argc, const pal::char_t* argv[]) { - pal::initialize_createdump(); +#if defined(FEATURE_STATIC_HOST) && (defined(TARGET_OSX) || defined(TARGET_LINUX)) && !defined(TARGET_X86) + extern void initialize_static_createdump(); + initialize_static_createdump(); +#endif pal::string_t host_path; if (!pal::get_own_executable_path(&host_path) || !pal::realpath(&host_path)) diff --git a/src/native/corehost/dotnet/CMakeLists.txt b/src/native/corehost/dotnet/CMakeLists.txt index d670b95e879c2..5704c911ac956 100644 --- a/src/native/corehost/dotnet/CMakeLists.txt +++ b/src/native/corehost/dotnet/CMakeLists.txt @@ -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. -project(dotnet) -set(DOTNET_PROJECT_NAME "dotnet") - if(CLR_CMAKE_TARGET_WIN32) list(APPEND SOURCES dotnet.manifest @@ -12,9 +9,21 @@ endif() list(APPEND SOURCES ../apphost/standalone/hostfxr_resolver.cpp + ../corehost.cpp ) -include(../exe.cmake) +add_executable(dotnet ${SOURCES}) + +target_link_libraries(dotnet PRIVATE hostmisc fxr_resolver) + +add_sanitizer_runtime_support(dotnet) + +if(NOT CLR_CMAKE_TARGET_WIN32) + disable_pax_mprotect(dotnet) +endif() + +install_with_stripped_symbols(dotnet TARGETS corehost) +add_version_info_to_target(dotnet) if (CLR_CMAKE_HOST_APPLE) adhoc_sign_with_entitlements(dotnet "${CLR_ENG_NATIVE_DIR}/entitlements.plist") diff --git a/src/native/corehost/exe.cmake b/src/native/corehost/exe.cmake deleted file mode 100644 index e4975923f1f23..0000000000000 --- a/src/native/corehost/exe.cmake +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project (${DOTNET_PROJECT_NAME}) - -include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/hostmisc/hostmisc.cmake) - -# Include directories -include_directories(${CMAKE_CURRENT_LIST_DIR}/fxr) - -# CMake does not recommend using globbing since it messes with the freshness checks -list(APPEND SOURCES - ${CMAKE_CURRENT_LIST_DIR}/fxr_resolver.cpp - ${CMAKE_CURRENT_LIST_DIR}/corehost.cpp -) -list(APPEND HEADERS - ${CMAKE_CURRENT_LIST_DIR}/hostfxr_resolver.h -) - -add_executable(${DOTNET_PROJECT_NAME} ${SOURCES} ${RESOURCES}) - -add_sanitizer_runtime_support(${DOTNET_PROJECT_NAME}) - -if(NOT CLR_CMAKE_TARGET_WIN32) - disable_pax_mprotect(${DOTNET_PROJECT_NAME}) -endif() - -install_with_stripped_symbols(${DOTNET_PROJECT_NAME} TARGETS corehost ${ADDITIONAL_INSTALL_ARGUMENTS}) - -set_common_libs("exe") diff --git a/src/native/corehost/fxr/standalone/CMakeLists.txt b/src/native/corehost/fxr/standalone/CMakeLists.txt index a809a653cce18..ceb0f9f417536 100644 --- a/src/native/corehost/fxr/standalone/CMakeLists.txt +++ b/src/native/corehost/fxr/standalone/CMakeLists.txt @@ -27,7 +27,11 @@ else(CLR_CMAKE_TARGET_WIN32) set_exports_linker_option(${EXPORTS_FILE}) endif(CLR_CMAKE_TARGET_WIN32) -include(../../lib.cmake) +add_compile_definitions(EXPORT_SHARED_API) + +add_library(hostfxr SHARED ${SOURCES}) + +add_version_info_to_target(hostfxr) if(CLR_CMAKE_HOST_UNIX) add_custom_target(hostfxr_exports DEPENDS ${EXPORTS_FILE}) @@ -41,6 +45,7 @@ install_with_stripped_symbols(hostfxr TARGETS corehost) target_link_libraries(hostfxr PRIVATE libhostcommon) -if (CLR_CMAKE_TARGET_ARCH_ARMV6) - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE atomic) +if(CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARMV6) + target_link_libraries(hostfxr PRIVATE + $<$:${ATOMIC_SUPPORT_LIB}>) endif() diff --git a/src/native/corehost/fxr/staticlib/CMakeLists.txt b/src/native/corehost/fxr/staticlib/CMakeLists.txt index 6ff3bc27f74c1..9ab84349d1adc 100644 --- a/src/native/corehost/fxr/staticlib/CMakeLists.txt +++ b/src/native/corehost/fxr/staticlib/CMakeLists.txt @@ -14,17 +14,24 @@ set(HEADERS ../hostpolicy_resolver.h ) +add_compile_definitions( + EXPORT_SHARED_API) + include(../../hostcommon/files.cmake) include(../files.cmake) -include(../../hostmisc/hostmisc.cmake) -include(../../lib_static.cmake) +add_library(libhostfxr STATIC ${SOURCES}) + +add_version_info_to_target(libhostfxr hostfxr) + +set_target_properties(libhostfxr PROPERTIES MACOSX_RPATH TRUE) +set_target_properties(libhostfxr PROPERTIES PREFIX "") # Copy static lib PDB to the project output directory if (WIN32) set_target_properties(libhostfxr PROPERTIES COMPILE_PDB_NAME "libhostfxr" - COMPILE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}" + COMPILE_PDB_OUTPUT_DIRECTORY "$" ) endif(WIN32) @@ -44,3 +51,5 @@ if (WIN32) else() install(TARGETS libhostfxr DESTINATION corehost) endif(WIN32) + +target_link_libraries(libhostfxr PRIVATE hostmisc) diff --git a/src/native/corehost/hostcommon/CMakeLists.txt b/src/native/corehost/hostcommon/CMakeLists.txt index 36b9d140cb95c..2d59348cad29c 100644 --- a/src/native/corehost/hostcommon/CMakeLists.txt +++ b/src/native/corehost/hostcommon/CMakeLists.txt @@ -1,12 +1,19 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(hostcommon) +include(files.cmake) -set(DOTNET_PROJECT_NAME "hostcommon") +add_library(libhostcommon STATIC ${SOURCES}) -include(files.cmake) +target_compile_definitions(libhostcommon PRIVATE EXPORT_SHARED_API) +set_target_properties(libhostcommon PROPERTIES MACOSX_RPATH TRUE) + +if (NOT CLR_CMAKE_TARGET_WIN32) + target_sources(libhostcommon PRIVATE ${VERSION_FILE_PATH}) +endif() -set(SKIP_VERSIONING 1) -include(../lib_static.cmake) +if (MSVC) + target_sources(libhostcommon PRIVATE ${HEADERS}) +endif() +target_link_libraries(libhostcommon PUBLIC hostmisc) diff --git a/src/native/corehost/hostcommon/files.cmake b/src/native/corehost/hostcommon/files.cmake index 43bd124b9b592..9b23d326f4657 100644 --- a/src/native/corehost/hostcommon/files.cmake +++ b/src/native/corehost/hostcommon/files.cmake @@ -11,7 +11,6 @@ list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/../roll_forward_option.cpp ${CMAKE_CURRENT_LIST_DIR}/../fx_definition.cpp ${CMAKE_CURRENT_LIST_DIR}/../fx_reference.cpp - ${CMAKE_CURRENT_LIST_DIR}/../fxr/fx_ver.cpp ${CMAKE_CURRENT_LIST_DIR}/../version_compatibility_range.cpp ${CMAKE_CURRENT_LIST_DIR}/../runtime_config.cpp ${CMAKE_CURRENT_LIST_DIR}/../bundle/info.cpp diff --git a/src/native/corehost/hostmisc/hostmisc.cmake b/src/native/corehost/hostmisc/CMakeLists.txt similarity index 53% rename from src/native/corehost/hostmisc/hostmisc.cmake rename to src/native/corehost/hostmisc/CMakeLists.txt index 83233a9572ff4..f39b586590c62 100644 --- a/src/native/corehost/hostmisc/hostmisc.cmake +++ b/src/native/corehost/hostmisc/CMakeLists.txt @@ -1,18 +1,16 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -include(${CMAKE_CURRENT_LIST_DIR}/configure.cmake) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories("${CLR_SRC_NATIVE_DIR}") +include(configure.cmake) # CMake does not recommend using globbing since it messes with the freshness checks -list(APPEND SOURCES +set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/trace.cpp ${CMAKE_CURRENT_LIST_DIR}/utils.cpp ${CMAKE_CURRENT_LIST_DIR}/../fxr/fx_ver.cpp ) -list(APPEND HEADERS +set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/trace.h ${CMAKE_CURRENT_LIST_DIR}/utils.h ${CMAKE_CURRENT_LIST_DIR}/pal.h @@ -30,3 +28,26 @@ else() list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/pal.unix.cpp) endif() + +# hostmisc must be an "object library" as we want to build it once +# and embed the objects into static libraries we ship (like libnethost). +add_library(hostmisc OBJECT ${SOURCES}) + +target_include_directories(hostmisc PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${CLR_SRC_NATIVE_DIR} + ${CMAKE_CURRENT_LIST_DIR}) + +if (MSVC) + target_sources(hostmisc PRIVATE ${HEADERS}) + target_link_libraries(hostmisc PUBLIC advapi32) +endif() + +target_link_libraries(hostmisc PUBLIC + ${CMAKE_DL_LIBS} + $<$:${PTHREAD_LIB}>) + +if(CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARMV6) + target_link_libraries(hostmisc PUBLIC + $<$:${ATOMIC_SUPPORT_LIB}>) +endif() diff --git a/src/native/corehost/hostmisc/pal.h b/src/native/corehost/hostmisc/pal.h index f482b6df63456..8aea41a2f07b4 100644 --- a/src/native/corehost/hostmisc/pal.h +++ b/src/native/corehost/hostmisc/pal.h @@ -58,6 +58,8 @@ #endif +#include "configure.h" + // When running on a platform that is not supported in RID fallback graph (because it was unknown // at the time the SharedFX in question was built), we need to use a reasonable fallback RID to allow // consuming the native assets. @@ -346,8 +348,6 @@ namespace pal bool is_emulating_x64(); bool are_paths_equal_with_normalized_casing(const string_t& path1, const string_t& path2); - - void initialize_createdump(); } #endif // PAL_H diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index bdd8796c5d8e0..b690b1f100ae8 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -1121,14 +1121,3 @@ bool pal::are_paths_equal_with_normalized_casing(const string_t& path1, const st return path1 == path2; #endif } - -#if defined(FEATURE_STATIC_HOST) && (defined(TARGET_OSX) || defined(TARGET_LINUX)) && !defined(TARGET_X86) -extern void initialize_static_createdump(); -#endif - -void pal::initialize_createdump() -{ -#if defined(FEATURE_STATIC_HOST) && (defined(TARGET_OSX) || defined(TARGET_LINUX)) && !defined(TARGET_X86) - initialize_static_createdump(); -#endif -} diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index b11610492d321..966725dc93880 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -941,7 +941,3 @@ void pal::mutex_t::unlock() { ::LeaveCriticalSection(&_impl); } - -void pal::initialize_createdump() -{ -} diff --git a/src/native/corehost/hostpolicy/standalone/CMakeLists.txt b/src/native/corehost/hostpolicy/standalone/CMakeLists.txt index f977c733d9968..31f4f8e8e3815 100644 --- a/src/native/corehost/hostpolicy/standalone/CMakeLists.txt +++ b/src/native/corehost/hostpolicy/standalone/CMakeLists.txt @@ -1,10 +1,6 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(hostpolicy) - -set(DOTNET_PROJECT_NAME "hostpolicy") - set(SOURCES ./coreclr_resolver.cpp ) @@ -22,7 +18,10 @@ else(CLR_CMAKE_TARGET_WIN32) set_exports_linker_option(${EXPORTS_FILE}) endif(CLR_CMAKE_TARGET_WIN32) -include(../../lib.cmake) +add_compile_definitions(EXPORT_SHARED_API) + +add_library(hostpolicy SHARED ${SOURCES}) +add_version_info_to_target(hostpolicy) if(CLR_CMAKE_HOST_UNIX) add_custom_target(hostpolicy_exports DEPENDS ${EXPORTS_FILE}) @@ -35,6 +34,7 @@ endif(CLR_CMAKE_HOST_UNIX) install_with_stripped_symbols(hostpolicy TARGETS corehost) target_link_libraries(hostpolicy PRIVATE libhostcommon) -if((CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_FREEBSD) AND NOT CLR_CMAKE_TARGET_ANDROID) - target_link_libraries (hostpolicy PRIVATE pthread) -endif() +if(CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARMV6) + target_link_libraries(hostpolicy INTERFACE + $<$:${ATOMIC_SUPPORT_LIB}>) +endif() \ No newline at end of file diff --git a/src/native/corehost/ijwhost/CMakeLists.txt b/src/native/corehost/ijwhost/CMakeLists.txt index 1661f162974d5..5e5218e578f3f 100644 --- a/src/native/corehost/ijwhost/CMakeLists.txt +++ b/src/native/corehost/ijwhost/CMakeLists.txt @@ -1,19 +1,13 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(ijwhost) - -set(DOTNET_PROJECT_NAME "ijwhost") - # Include directories -include_directories(../fxr) include_directories(${ARCH_SOURCES_DIR}) # CMake does not recommend using globbing since it messes with the freshness checks set(SOURCES ijwthunk.cpp ijwhost.cpp - ../fxr_resolver.cpp pedecoder.cpp bootstrap_thunk_chunk.cpp ${ARCH_SOURCES_DIR}/bootstrap_thunk.cpp @@ -27,11 +21,10 @@ endif() set (ASM_HELPERS_SOURCES ${ARCH_SOURCES_DIR}/asmhelpers.asm) -add_definitions(-DFEATURE_LIBHOST=1) - -convert_to_absolute_path(SOURCES ${SOURCES}) convert_to_absolute_path(ASM_HELPERS_SOURCES ${ASM_HELPERS_SOURCES}) +add_compile_definitions(FEATURE_LIBHOST) + if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64) preprocess_files(ASM_HELPERS_SOURCES ${ASM_HELPERS_SOURCES}) if (CMAKE_GENERATOR MATCHES "Visual Studio") @@ -45,11 +38,16 @@ endif () list(APPEND SOURCES ${ASM_HELPERS_SOURCES}) -include(../lib.cmake) +add_compile_definitions(EXPORT_SHARED_API) + +add_library(ijwhost SHARED ${SOURCES} ${RESOURCES}) +add_version_info_to_target(ijwhost) + +target_link_libraries(ijwhost PRIVATE hostmisc fxr_resolver) # Specify non-default Windows libs to be used for Arm64 builds if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64) - target_link_libraries(ijwhost PRIVATE Ole32.lib) + target_link_libraries(ijwhost PRIVATE ole32) endif() install_with_stripped_symbols(ijwhost TARGETS corehost) diff --git a/src/native/corehost/lib.cmake b/src/native/corehost/lib.cmake deleted file mode 100644 index e61eb0a2b3900..0000000000000 --- a/src/native/corehost/lib.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(${DOTNET_PROJECT_NAME}) - -include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/hostmisc/hostmisc.cmake) - -add_definitions(-D_NO_ASYNCRTIMP) -add_definitions(-D_NO_PPLXIMP) -add_definitions(-DEXPORT_SHARED_API=1) - -add_library(${DOTNET_PROJECT_NAME} SHARED ${SOURCES} ${RESOURCES}) - -set_target_properties(${DOTNET_PROJECT_NAME} PROPERTIES MACOSX_RPATH TRUE) - -set_common_libs("lib") diff --git a/src/native/corehost/lib_static.cmake b/src/native/corehost/lib_static.cmake deleted file mode 100644 index b6d8744e94e0e..0000000000000 --- a/src/native/corehost/lib_static.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(lib${DOTNET_PROJECT_NAME}) - -include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) - -add_definitions(-D_NO_ASYNCRTIMP) -add_definitions(-D_NO_PPLXIMP) -add_definitions(-DEXPORT_SHARED_API=1) - -if (BUILD_OBJECT_LIBRARY) - add_library(lib${DOTNET_PROJECT_NAME} OBJECT ${SOURCES} ${RESOURCES}) -else () - add_library(lib${DOTNET_PROJECT_NAME} STATIC ${SOURCES} ${RESOURCES}) -endif () - -set_target_properties(lib${DOTNET_PROJECT_NAME} PROPERTIES MACOSX_RPATH TRUE) -set_target_properties(lib${DOTNET_PROJECT_NAME} PROPERTIES PREFIX "") - -set_common_libs("lib-static") diff --git a/src/native/corehost/nethost/CMakeLists.txt b/src/native/corehost/nethost/CMakeLists.txt index 818ed9ced7fce..38b103b7178be 100644 --- a/src/native/corehost/nethost/CMakeLists.txt +++ b/src/native/corehost/nethost/CMakeLists.txt @@ -1,17 +1,9 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(nethost) - -set(DOTNET_PROJECT_NAME "nethost") - -# Include directories -include_directories(../fxr) - # CMake does not recommend using globbing since it messes with the freshness checks set(SOURCES nethost.cpp - ../fxr_resolver.cpp ) if(CLR_CMAKE_TARGET_WIN32) @@ -19,20 +11,40 @@ if(CLR_CMAKE_TARGET_WIN32) Exports.def) endif() -include(../lib.cmake) -include(../lib_static.cmake) +add_compile_definitions( + EXPORT_SHARED_API + FEATURE_LIBHOST + NETHOST_EXPORT) + +add_library(nethost SHARED ${SOURCES}) +add_library(libnethost STATIC ${SOURCES}) -add_definitions(-DFEATURE_LIBHOST=1) -add_definitions(-DNETHOST_EXPORT) +add_version_info_to_target(nethost) +add_version_info_to_target(libnethost nethost) + +target_include_directories(nethost PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(libnethost PUBLIC $) # Copy static lib PDB to the project output directory if (WIN32) set_target_properties(libnethost PROPERTIES COMPILE_PDB_NAME "libnethost" - COMPILE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}" + COMPILE_PDB_OUTPUT_DIRECTORY "$" ) endif(WIN32) +target_link_libraries(nethost PRIVATE hostmisc fxr_resolver) +target_link_libraries(libnethost PRIVATE hostmisc fxr_resolver) +target_compile_definitions(nethost PRIVATE FEATURE_LIBHOST NETHOST_EXPORT) +target_compile_definitions(libnethost PRIVATE FEATURE_LIBHOST NETHOST_EXPORT) + +set_target_properties(nethost PROPERTIES MACOSX_RPATH TRUE) +set_target_properties(libnethost PROPERTIES MACOSX_RPATH TRUE) +set_target_properties(libnethost PROPERTIES PREFIX "") + +target_link_libraries (nethost PRIVATE ${CMAKE_DL_LIBS}) +target_link_libraries (libnethost PUBLIC ${CMAKE_DL_LIBS}) + install(FILES ../coreclr_delegates.h DESTINATION corehost) install(FILES ../hostfxr.h DESTINATION corehost) install(FILES nethost.h DESTINATION corehost) diff --git a/src/native/corehost/setup.cmake b/src/native/corehost/setup.cmake deleted file mode 100644 index 787d2ca775d3c..0000000000000 --- a/src/native/corehost/setup.cmake +++ /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. - -if(CLR_SINGLE_FILE_HOST_ONLY) - # CLR partition builds only the single file host where hosting components are all statically linked. - # the versioning information is irrelevant and may only come up in tracing. - # so we will use "static" - add_definitions(-DHOST_POLICY_PKG_NAME="static") - add_definitions(-DHOST_POLICY_PKG_REL_DIR="static") - add_definitions(-DREPO_COMMIT_HASH="static") -else() - if("${CLI_CMAKE_PKG_RID}" STREQUAL "") - message(FATAL_ERROR "A minimum supported package rid is not specified (ex: win7-x86 or ubuntu.14.04-x64, osx.10.12-x64, rhel.7-x64)") - else() - add_definitions(-DHOST_POLICY_PKG_NAME="runtime.${CLI_CMAKE_PKG_RID}.Microsoft.NETCore.DotNetHostPolicy") - add_definitions(-DHOST_POLICY_PKG_REL_DIR="runtimes/${CLI_CMAKE_PKG_RID}/native") - endif() - - if("${CLI_CMAKE_COMMIT_HASH}" STREQUAL "") - message(FATAL_ERROR "Commit hash needs to be specified to build the host") - else() - add_definitions(-DREPO_COMMIT_HASH="${CLI_CMAKE_COMMIT_HASH}") - endif() -endif() - -if("${CLI_CMAKE_FALLBACK_OS}" STREQUAL "") - message(FATAL_ERROR "Fallback rid needs to be specified to build the host") -else() - add_definitions(-DFALLBACK_HOST_OS="${CLI_CMAKE_FALLBACK_OS}") -endif() - -add_definitions(-DCURRENT_OS_NAME="${CLR_CMAKE_TARGET_OS}") -add_definitions(-DCURRENT_ARCH_NAME="${CLR_CMAKE_TARGET_ARCH}") -if("${CLI_CMAKE_FALLBACK_OS}" STREQUAL "${CLR_CMAKE_TARGET_OS}") - add_definitions(-DFALLBACK_OS_IS_SAME_AS_TARGET_OS) -endif() diff --git a/src/native/corehost/test/CMakeLists.txt b/src/native/corehost/test/CMakeLists.txt index fac7e76baf6a7..4310a0e8449b3 100644 --- a/src/native/corehost/test/CMakeLists.txt +++ b/src/native/corehost/test/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(fx_ver) add_subdirectory(mockcoreclr) -add_subdirectory(mockhostfxr/2_2) -add_subdirectory(mockhostfxr/5_0) +add_subdirectory(mockhostfxr) add_subdirectory(mockhostpolicy) add_subdirectory(nativehost) if (NOT RUNTIME_FLAVOR STREQUAL Mono) diff --git a/src/native/corehost/test/comsxs/CMakeLists.txt b/src/native/corehost/test/comsxs/CMakeLists.txt index 673e7f07bdde0..144937e9f8152 100644 --- a/src/native/corehost/test/comsxs/CMakeLists.txt +++ b/src/native/corehost/test/comsxs/CMakeLists.txt @@ -1,18 +1,12 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(comsxs) +add_executable(comsxs + comsxs.cpp + App.manifest) -set(DOTNET_PROJECT_NAME "comsxs") +add_sanitizer_runtime_support(comsxs) -set(SOURCES - ./comsxs.cpp - ./App.manifest -) - - -include(../testexe.cmake) - -target_link_libraries(comsxs PRIVATE ole32 oleaut32) +target_link_libraries(comsxs PRIVATE hostmisc ole32 oleaut32 shell32 advapi32) install_with_stripped_symbols(comsxs TARGETS corehost_test) diff --git a/src/native/corehost/test/fx_ver/CMakeLists.txt b/src/native/corehost/test/fx_ver/CMakeLists.txt index ed39b965966c6..a6bb1421dc7ca 100644 --- a/src/native/corehost/test/fx_ver/CMakeLists.txt +++ b/src/native/corehost/test/fx_ver/CMakeLists.txt @@ -1,18 +1,11 @@ # Copyright (c) .NET Foundation and contributors. All rights reserved. # Licensed under the MIT license. See LICENSE file in the project root for full license information. - -project(test_fx_ver) - -set(DOTNET_PROJECT_NAME "test_fx_ver") - include_directories(../../fxr) -set(SOURCES - test_fx_ver.cpp -) +add_executable(test_fx_ver test_fx_ver.cpp) -include(${CMAKE_CURRENT_LIST_DIR}/../../hostmisc/hostmisc.cmake) +add_sanitizer_runtime_support(test_fx_ver) -include(../testexe.cmake) +target_link_libraries(test_fx_ver PRIVATE libhostcommon hostmisc) -target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE libhostcommon) +install_with_stripped_symbols(test_fx_ver TARGETS corehost_test) diff --git a/src/native/corehost/test/ijw/CMakeLists.txt b/src/native/corehost/test/ijw/CMakeLists.txt index 9e90cfdcf069a..9fbd72412d4c8 100644 --- a/src/native/corehost/test/ijw/CMakeLists.txt +++ b/src/native/corehost/test/ijw/CMakeLists.txt @@ -1,15 +1,10 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(ijw) - -set(SOURCES - ./ijw.cpp) - include(${CLR_ENG_NATIVE_DIR}/ijw/IJW.cmake) -add_library(ijw SHARED ${SOURCES}) -target_link_libraries(ijw PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) +add_library(ijw SHARED ijw.cpp) +target_link_libraries(ijw PRIVATE ijwhost) remove_ijw_incompatible_target_options(ijw) add_ijw_msbuild_project_properties(ijw ijwhost) diff --git a/src/native/corehost/test/mockcoreclr/CMakeLists.txt b/src/native/corehost/test/mockcoreclr/CMakeLists.txt index f7e202fbc1dff..5d809d84b5d56 100644 --- a/src/native/corehost/test/mockcoreclr/CMakeLists.txt +++ b/src/native/corehost/test/mockcoreclr/CMakeLists.txt @@ -1,19 +1,15 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(mockcoreclr) - -set(DOTNET_PROJECT_NAME "mockcoreclr") - -set(SOURCES - ./mockcoreclr.cpp -) +add_library(mockcoreclr SHARED mockcoreclr.cpp) if(CLR_CMAKE_TARGET_WIN32) - list(APPEND SOURCES + target_sources(mockcoreclr PRIVATE mockcoreclr.def) endif() -include(../testlib.cmake) +target_link_libraries(mockcoreclr PRIVATE hostmisc) + +target_compile_definitions(mockcoreclr PRIVATE EXPORT_SHARED_API) install_with_stripped_symbols(mockcoreclr TARGETS corehost_test) diff --git a/src/native/corehost/test/mockhostfxr/2_2/CMakeLists.txt b/src/native/corehost/test/mockhostfxr/2_2/CMakeLists.txt deleted file mode 100644 index 096c3631afbc8..0000000000000 --- a/src/native/corehost/test/mockhostfxr/2_2/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(mockhostfxr_2_2) - -set(DOTNET_PROJECT_NAME "mockhostfxr_2_2") - -add_definitions(-D_MOCKHOSTFXR_2_2) - -set(SOURCES - ./../mockhostfxr.cpp -) - -include(../../testlib.cmake) - -install_with_stripped_symbols(mockhostfxr_2_2 TARGETS corehost_test) diff --git a/src/native/corehost/test/mockhostfxr/5_0/CMakeLists.txt b/src/native/corehost/test/mockhostfxr/5_0/CMakeLists.txt deleted file mode 100644 index 5f9f36c71852a..0000000000000 --- a/src/native/corehost/test/mockhostfxr/5_0/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(mockhostfxr_5_0) - -set(DOTNET_PROJECT_NAME "mockhostfxr_5_0") - -add_definitions(-DMOCKHOSTFXR_5_0) - -set(SOURCES - ./../mockhostfxr.cpp -) - -include(../../testlib.cmake) - -install_with_stripped_symbols(mockhostfxr_5_0 TARGETS corehost_test) diff --git a/src/native/corehost/test/mockhostfxr/CMakeLists.txt b/src/native/corehost/test/mockhostfxr/CMakeLists.txt new file mode 100644 index 0000000000000..6820b27d84715 --- /dev/null +++ b/src/native/corehost/test/mockhostfxr/CMakeLists.txt @@ -0,0 +1,14 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the MIT license. + +add_library(mockhostfxr_2_2 SHARED mockhostfxr.cpp) +add_library(mockhostfxr_5_0 SHARED mockhostfxr.cpp) + +target_link_libraries(mockhostfxr_2_2 PRIVATE libhostcommon) +target_link_libraries(mockhostfxr_5_0 PRIVATE libhostcommon) + +target_compile_definitions(mockhostfxr_2_2 PRIVATE MOCKHOSTFXR_2_2 EXPORT_SHARED_API) +target_compile_definitions(mockhostfxr_5_0 PRIVATE MOCKHOSTFXR_5_0 EXPORT_SHARED_API) + +install_with_stripped_symbols(mockhostfxr_2_2 TARGETS corehost_test) +install_with_stripped_symbols(mockhostfxr_5_0 TARGETS corehost_test) \ No newline at end of file diff --git a/src/native/corehost/test/mockhostpolicy/CMakeLists.txt b/src/native/corehost/test/mockhostpolicy/CMakeLists.txt index 146cf010e3fa6..d89c06d8d563d 100644 --- a/src/native/corehost/test/mockhostpolicy/CMakeLists.txt +++ b/src/native/corehost/test/mockhostpolicy/CMakeLists.txt @@ -1,14 +1,10 @@ # Licensed to the .NET Foundation under one or more agreements. # The .NET Foundation licenses this file to you under the MIT license. -project(mockhostpolicy) +add_library(mockhostpolicy SHARED mockhostpolicy.cpp) -set(DOTNET_PROJECT_NAME "mockhostpolicy") +target_link_libraries(mockhostpolicy PRIVATE hostmisc) -set(SOURCES - ./mockhostpolicy.cpp -) - -include(../testlib.cmake) +target_compile_definitions(mockhostpolicy PRIVATE EXPORT_SHARED_API) install_with_stripped_symbols(mockhostpolicy TARGETS corehost_test) \ No newline at end of file diff --git a/src/native/corehost/test/nativehost/CMakeLists.txt b/src/native/corehost/test/nativehost/CMakeLists.txt index 527ab578d3991..a3c9ed9fa24a3 100644 --- a/src/native/corehost/test/nativehost/CMakeLists.txt +++ b/src/native/corehost/test/nativehost/CMakeLists.txt @@ -3,8 +3,6 @@ project(nativehost) -set(DOTNET_PROJECT_NAME "nativehost") - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(MACOSX_RPATH ON) if (CLR_CMAKE_TARGET_OSX) @@ -13,8 +11,6 @@ else() set(CMAKE_INSTALL_RPATH "\$ORIGIN") endif() -include_directories(${CMAKE_CURRENT_LIST_DIR}/../../nethost) - set(SOURCES ./error_writer_redirector.cpp ./get_native_search_directories_test.cpp @@ -40,25 +36,22 @@ if(CLR_CMAKE_TARGET_WIN32) list(APPEND HEADERS ./comhost_test.h) - - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:nethost.dll") endif() -include(../../hostmisc/hostmisc.cmake) +add_executable(nativehost ${SOURCES}) -include(../testexe.cmake) +add_sanitizer_runtime_support(nativehost) -target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE nethost) +install_with_stripped_symbols(nativehost TARGETS corehost_test) -if (CLR_CMAKE_TARGET_WIN32) - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE delayimp.lib) -endif() +target_link_libraries(nativehost PRIVATE + $<$:${PTHREAD_LIB}>) -# Specify non-default Windows libs to be used for Arm/Arm64 builds -if (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARM64)) - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE Ole32.lib OleAut32.lib) -endif() -if (CLR_CMAKE_TARGET_ARCH_ARMV6) - target_link_libraries(${DOTNET_PROJECT_NAME} PRIVATE atomic) +target_link_libraries(nativehost PRIVATE nethost hostmisc) + +if (CLR_CMAKE_TARGET_WIN32) + target_link_options(nativehost PRIVATE /DELAYLOAD:$) + target_link_libraries(nativehost PRIVATE delayimp.lib ole32 oleaut32) endif() + diff --git a/src/native/corehost/test/nativehost/comhost_test.cpp b/src/native/corehost/test/nativehost/comhost_test.cpp index 53cc493a78b3a..b93a114f4363b 100644 --- a/src/native/corehost/test/nativehost/comhost_test.cpp +++ b/src/native/corehost/test/nativehost/comhost_test.cpp @@ -209,7 +209,7 @@ bool comhost_test::typelib(const pal::string_t &comhost_path, int count) for (int i = 1; i < count + 1; i++) { // The path format for a non-default embedded TLB is 'C:\file\path\to.exe\\2' where '2' is the resource name of the tlb to load. - // See https://docs.microsoft.com/windows/win32/api/oleauto/nf-oleauto-loadtypelib#remarks for documentation on the path format. + // See https://learn.microsoft.com/windows/win32/api/oleauto/nf-oleauto-loadtypelib#remarks for documentation on the path format. pal::stringstream_t tlb_path; tlb_path << comhost_path << '\\' << i; hr = load_typelib(tlb_path.str()); diff --git a/src/native/corehost/test/testexe.cmake b/src/native/corehost/test/testexe.cmake deleted file mode 100644 index 75538c7090a67..0000000000000 --- a/src/native/corehost/test/testexe.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(${DOTNET_PROJECT_NAME}) - -set(SKIP_VERSIONING 1) - -include(${CMAKE_CURRENT_LIST_DIR}/../common.cmake) - -add_executable(${DOTNET_PROJECT_NAME} ${SOURCES}) - -add_sanitizer_runtime_support(${DOTNET_PROJECT_NAME}) - -install_with_stripped_symbols(${DOTNET_PROJECT_NAME} TARGETS corehost_test) - -set_common_libs("exe") diff --git a/src/native/corehost/test/testlib.cmake b/src/native/corehost/test/testlib.cmake deleted file mode 100644 index 6031136fb9750..0000000000000 --- a/src/native/corehost/test/testlib.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -project(${DOTNET_PROJECT_NAME}) - -set(SKIP_VERSIONING 1) - -include(${CMAKE_CURRENT_LIST_DIR}/../lib.cmake) diff --git a/src/native/eventpipe/README.md b/src/native/eventpipe/README.md index eae5604bf8c45..1fb84f172639f 100644 --- a/src/native/eventpipe/README.md +++ b/src/native/eventpipe/README.md @@ -7,7 +7,7 @@ If there is information you wish was here and it isn't, please add it :) EventPipe is a cross-platform eventing library written in C with significant inspiration from ETW on Windows. Previously when .NET primarily ran on Windows we relied solely on ETW, but now that we run on multiple platforms we wanted to have cross platform logging supported directly in the -runtime. For more info see [the docs](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/eventpipe). +runtime. For more info see [the docs](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe). DiagnosticServer is a simple RPC mechanism that allows external tools to communicate with the runtime over named pipes and sockets. External tools send the runtime commands which the runtime diff --git a/src/native/external/llvm-libunwind-version.txt b/src/native/external/llvm-libunwind-version.txt index d23999cc45368..f833dd0c83fb5 100644 --- a/src/native/external/llvm-libunwind-version.txt +++ b/src/native/external/llvm-libunwind-version.txt @@ -1,6 +1,5 @@ -v16.0.2 -https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.2 +v18.1.5 +https://github.com/llvm/llvm-project/releases/tag/llvmorg-18.1.5 -Apply https://github.com/dotnet/runtime/commit/1bafb60792b91747d9c2a9dd38461cf090a987a4 -Apply https://github.com/dotnet/runtime/commit/0ee8827547405408b37d1aae2a83629c1632eea8 +Apply https://github.com/dotnet/runtime/commit/a53805790a49f5bf52776dc81c1c83ec7f21ebce diff --git a/src/native/external/llvm-libunwind.cmake b/src/native/external/llvm-libunwind.cmake index 72cecd2a14980..720dc5ae6f890 100644 --- a/src/native/external/llvm-libunwind.cmake +++ b/src/native/external/llvm-libunwind.cmake @@ -9,13 +9,6 @@ set (LLVM_LIBUNWIND_SOURCES_BASE src/libunwind.cpp ) -if(CLR_CMAKE_TARGET_APPLE) - set(LLVM_LIBUNWIND_SOURCES_BASE - ${LLVM_LIBUNWIND_SOURCES_BASE} - src/Unwind_AppleExtras.cpp - ) -endif() - set(LLVM_LIBUNWIND_ASM_SOURCES_BASE src/UnwindRegistersRestore.S src/UnwindRegistersSave.S diff --git a/src/native/external/llvm-libunwind/CMakeLists.txt b/src/native/external/llvm-libunwind/CMakeLists.txt index 5c547883f9927..806d5a783ec39 100644 --- a/src/native/external/llvm-libunwind/CMakeLists.txt +++ b/src/native/external/llvm-libunwind/CMakeLists.txt @@ -2,7 +2,7 @@ # Setup Project #=============================================================================== -cmake_minimum_required(VERSION 3.13.4) +cmake_minimum_required(VERSION 3.20.0) set(LLVM_COMMON_CMAKE_UTILS "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") @@ -10,6 +10,7 @@ set(LLVM_COMMON_CMAKE_UTILS "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + "${CMAKE_CURRENT_SOURCE_DIR}/../runtimes/cmake/Modules" "${LLVM_COMMON_CMAKE_UTILS}" "${LLVM_COMMON_CMAKE_UTILS}/Modules" ) @@ -20,6 +21,7 @@ set(LIBUNWIND_LIBCXX_PATH "${CMAKE_CURRENT_LIST_DIR}/../libcxx" CACHE PATH "Specify path to libc++ source.") include(GNUInstallDirs) +include(CheckSymbolExists) #=============================================================================== # Setup CMake Options @@ -50,6 +52,7 @@ option(LIBUNWIND_IS_BAREMETAL "Build libunwind for baremetal targets." OFF) option(LIBUNWIND_USE_FRAME_HEADER_CACHE "Cache frame headers for unwinding. Requires locking dl_iterate_phdr." OFF) option(LIBUNWIND_REMEMBER_HEAP_ALLOC "Use heap instead of the stack for .cfi_remember_state." OFF) option(LIBUNWIND_INSTALL_HEADERS "Install the libunwind headers." ON) +option(LIBUNWIND_ENABLE_FRAME_APIS "Include libgcc-compatible frame apis." OFF) set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING "Define suffix of library directory name (32/64)") @@ -61,12 +64,9 @@ cmake_dependent_option(LIBUNWIND_INSTALL_SHARED_LIBRARY "Install the shared libunwind library." ON "LIBUNWIND_ENABLE_SHARED;LIBUNWIND_INSTALL_LIBRARY" OFF) -# TODO: Remove this after branching for LLVM 15 -if(LIBUNWIND_SYSROOT OR LIBUNWIND_TARGET_TRIPLE OR LIBUNWIND_GCC_TOOLCHAIN) - message(WARNING "LIBUNWIND_SYSROOT, LIBUNWIND_TARGET_TRIPLE and LIBUNWIND_GCC_TOOLCHAIN are not supported anymore, please use the native CMake equivalents instead") -endif() - -if (LIBUNWIND_ENABLE_SHARED) +if(MINGW) + set(LIBUNWIND_DEFAULT_TEST_CONFIG "llvm-libunwind-mingw.cfg.in") +elseif (LIBUNWIND_ENABLE_SHARED) set(LIBUNWIND_DEFAULT_TEST_CONFIG "llvm-libunwind-shared.cfg.in") else() set(LIBUNWIND_DEFAULT_TEST_CONFIG "llvm-libunwind-static.cfg.in") @@ -97,6 +97,20 @@ endif() option(LIBUNWIND_HIDE_SYMBOLS "Do not export any symbols from the static library." ${LIBUNWIND_DEFAULT_HIDE_SYMBOLS}) +# If toolchain is FPXX, we switch to FP64 to save the full FPRs. See: +# https://web.archive.org/web/20180828210612/https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking +check_symbol_exists(__mips_hard_float "" __MIPSHF) +check_symbol_exists(_ABIO32 "" __MIPS_O32) +if (__MIPSHF AND __MIPS_O32) + file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/mips_is_fpxx.c + "#if __mips_fpr != 0\n" + "# error\n" + "#endif\n") + try_compile(MIPS_FPABI_FPXX ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/mips_is_fpxx.c + CMAKE_FLAGS -DCMAKE_C_LINK_EXECUTABLE='echo') +endif() + #=============================================================================== # Configure System #=============================================================================== @@ -106,9 +120,9 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -set(LIBUNWIND_INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE PATH +set(LIBUNWIND_INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE STRING "Path where built libunwind headers should be installed.") -set(LIBUNWIND_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE PATH +set(LIBUNWIND_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING "Path where built libunwind runtime libraries should be installed.") set(LIBUNWIND_SHARED_OUTPUT_NAME "unwind" CACHE STRING "Output name for the shared libunwind runtime library.") @@ -116,7 +130,7 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}) - set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE} CACHE PATH + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE} CACHE STRING "Path where built libunwind libraries should be installed.") if(LIBCXX_LIBDIR_SUBDIR) string(APPEND LIBUNWIND_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR}) @@ -128,7 +142,7 @@ else() else() set(LIBUNWIND_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBUNWIND_LIBDIR_SUFFIX}) endif() - set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX} CACHE PATH + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX} CACHE STRING "Path where built libunwind libraries should be installed.") endif() @@ -140,6 +154,10 @@ set(LIBUNWIND_C_FLAGS "") set(LIBUNWIND_CXX_FLAGS "") set(LIBUNWIND_COMPILE_FLAGS "") set(LIBUNWIND_LINK_FLAGS "") +set(LIBUNWIND_ADDITIONAL_COMPILE_FLAGS "" CACHE STRING + "Additional Compile only flags which can be provided in cache") +set(LIBUNWIND_ADDITIONAL_LIBRARIES "" CACHE STRING + "Additional libraries libunwind is linked to which can be provided in cache") # Include macros for adding and removing libunwind flags. include(HandleLibunwindFlags) @@ -168,28 +186,6 @@ if (LIBUNWIND_ENABLE_CET) endif() endif() -# Get warning flags -add_compile_flags_if_supported(-W) -add_compile_flags_if_supported(-Wall) -add_compile_flags_if_supported(-Wchar-subscripts) -add_compile_flags_if_supported(-Wconversion) -add_compile_flags_if_supported(-Wmismatched-tags) -add_compile_flags_if_supported(-Wmissing-braces) -add_compile_flags_if_supported(-Wnewline-eof) -add_compile_flags_if_supported(-Wno-unused-function) -add_compile_flags_if_supported(-Wshadow) -add_compile_flags_if_supported(-Wshorten-64-to-32) -add_compile_flags_if_supported(-Wsign-compare) -add_compile_flags_if_supported(-Wsign-conversion) -add_compile_flags_if_supported(-Wstrict-aliasing=2) -add_compile_flags_if_supported(-Wstrict-overflow=4) -add_compile_flags_if_supported(-Wunused-parameter) -add_compile_flags_if_supported(-Wunused-variable) -add_compile_flags_if_supported(-Wwrite-strings) -add_compile_flags_if_supported(-Wundef) - -add_compile_flags_if_supported(-Wno-suggest-override) - if (WIN32) # The headers lack matching dllexport attributes (_LIBUNWIND_EXPORT); # silence the warning instead of cluttering the headers (which aren't @@ -198,16 +194,8 @@ if (WIN32) add_compile_flags_if_supported(-Wno-dll-attribute-on-redeclaration) endif() -if (LIBUNWIND_ENABLE_WERROR) - add_compile_flags_if_supported(-Werror) - add_compile_flags_if_supported(-WX) -else() - add_compile_flags_if_supported(-Wno-error) - add_compile_flags_if_supported(-WX-) -endif() - -if (LIBUNWIND_ENABLE_PEDANTIC) - add_compile_flags_if_supported(-pedantic) +if (MIPS_FPABI_FPXX) + add_compile_flags(-mfp64) endif() # Get feature flags. @@ -277,6 +265,11 @@ if (NOT LIBUNWIND_ENABLE_CROSS_UNWINDING) add_compile_flags(-D_LIBUNWIND_IS_NATIVE_ONLY) endif() +# Include stubs for __register_frame_info_bases and related +if (LIBUNWIND_ENABLE_FRAME_APIS) + add_compile_flags(-D_LIBUNWIND_SUPPORT_FRAME_APIS) +endif() + # Threading-support if (NOT LIBUNWIND_ENABLE_THREADS) add_compile_flags(-D_LIBUNWIND_HAS_NO_THREADS) diff --git a/src/native/external/llvm-libunwind/cmake/Modules/HandleLibunwindFlags.cmake b/src/native/external/llvm-libunwind/cmake/Modules/HandleLibunwindFlags.cmake index c5d76034d8709..94c676338821c 100644 --- a/src/native/external/llvm-libunwind/cmake/Modules/HandleLibunwindFlags.cmake +++ b/src/native/external/llvm-libunwind/cmake/Modules/HandleLibunwindFlags.cmake @@ -6,162 +6,10 @@ include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) +include(HandleFlags) unset(add_flag_if_supported) -# Mangle the name of a compiler flag into a valid CMake identifier. -# Ex: --std=c++11 -> STD_EQ_CXX11 -macro(mangle_name str output) - string(STRIP "${str}" strippedStr) - string(REGEX REPLACE "^/" "" strippedStr "${strippedStr}") - string(REGEX REPLACE "^-+" "" strippedStr "${strippedStr}") - string(REGEX REPLACE "-+$" "" strippedStr "${strippedStr}") - string(REPLACE "-" "_" strippedStr "${strippedStr}") - string(REPLACE "=" "_EQ_" strippedStr "${strippedStr}") - string(REPLACE "+" "X" strippedStr "${strippedStr}") - string(TOUPPER "${strippedStr}" ${output}) -endmacro() - -# Remove a list of flags from all CMake variables that affect compile flags. -# This can be used to remove unwanted flags specified on the command line -# or added in other parts of LLVM's cmake configuration. -macro(remove_flags) - foreach(var ${ARGN}) - string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") - string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - string(REPLACE "${var}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - string(REPLACE "${var}" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") - string(REPLACE "${var}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") - string(REPLACE "${var}" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") - string(REPLACE "${var}" "" CMAKE_SHARED_MODULE_FLAGS "${CMAKE_SHARED_MODULE_FLAGS}") - remove_definitions(${var}) - endforeach() -endmacro(remove_flags) - -macro(check_flag_supported flag) - mangle_name("${flag}" flagname) - check_cxx_compiler_flag("${flag}" "CXX_SUPPORTS_${flagname}_FLAG") -endmacro() - -macro(append_flags DEST) - foreach(value ${ARGN}) - list(APPEND ${DEST} ${value}) - list(APPEND ${DEST} ${value}) - endforeach() -endmacro() - -# If the specified 'condition' is true then append the specified list of flags to DEST -macro(append_flags_if condition DEST) - if (${condition}) - list(APPEND ${DEST} ${ARGN}) - endif() -endmacro() - -# Add each flag in the list specified by DEST if that flag is supported by the current compiler. -macro(append_flags_if_supported DEST) - foreach(flag ${ARGN}) - mangle_name("${flag}" flagname) - check_cxx_compiler_flag("${flag}" "CXX_SUPPORTS_${flagname}_FLAG") - append_flags_if(CXX_SUPPORTS_${flagname}_FLAG ${DEST} ${flag}) - endforeach() -endmacro() - -# Add a macro definition if condition is true. -macro(define_if condition def) - if (${condition}) - add_definitions(${def}) - endif() -endmacro() - -# Add a macro definition if condition is not true. -macro(define_if_not condition def) - if (NOT ${condition}) - add_definitions(${def}) - endif() -endmacro() - -# Add a macro definition to the __config_site file if the specified condition -# is 'true'. Note that '-D${def}' is not added. Instead it is expected that -# the build include the '__config_site' header. -macro(config_define_if condition def) - if (${condition}) - set(${def} ON) - set(LIBUNWIND_NEEDS_SITE_CONFIG ON) - endif() -endmacro() - -macro(config_define_if_not condition def) - if (NOT ${condition}) - set(${def} ON) - set(LIBUNWIND_NEEDS_SITE_CONFIG ON) - endif() -endmacro() - -macro(config_define value def) - set(${def} ${value}) - set(LIBUNWIND_NEEDS_SITE_CONFIG ON) -endmacro() - -# Add a list of flags to all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', -# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'. -macro(add_target_flags) - foreach(value ${ARGN}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${value}") - list(APPEND LIBUNWIND_COMPILE_FLAGS ${value}) - list(APPEND LIBUNWIND_LINK_FLAGS ${value}) - endforeach() -endmacro() - -# If the specified 'condition' is true then add a list of flags to -# all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBUNWIND_COMPILE_FLAGS' -# and 'LIBUNWIND_LINK_FLAGS'. -macro(add_target_flags_if condition) - if (${condition}) - add_target_flags(${ARGN}) - endif() -endmacro() - -# Add all the flags supported by the compiler to all of -# 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBUNWIND_COMPILE_FLAGS' -# and 'LIBUNWIND_LINK_FLAGS'. -macro(add_target_flags_if_supported) - foreach(flag ${ARGN}) - mangle_name("${flag}" flagname) - check_cxx_compiler_flag("${flag}" "CXX_SUPPORTS_${flagname}_FLAG") - add_target_flags_if(CXX_SUPPORTS_${flagname}_FLAG ${flag}) - endforeach() -endmacro() - -# Add a specified list of flags to both 'LIBUNWIND_COMPILE_FLAGS' and -# 'LIBUNWIND_LINK_FLAGS'. -macro(add_flags) - foreach(value ${ARGN}) - list(APPEND LIBUNWIND_COMPILE_FLAGS ${value}) - list(APPEND LIBUNWIND_LINK_FLAGS ${value}) - endforeach() -endmacro() - -# If the specified 'condition' is true then add a list of flags to both -# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'. -macro(add_flags_if condition) - if (${condition}) - add_flags(${ARGN}) - endif() -endmacro() - -# Add each flag in the list to LIBUNWIND_COMPILE_FLAGS and LIBUNWIND_LINK_FLAGS -# if that flag is supported by the current compiler. -macro(add_flags_if_supported) - foreach(flag ${ARGN}) - mangle_name("${flag}" flagname) - check_cxx_compiler_flag("${flag}" "CXX_SUPPORTS_${flagname}_FLAG") - add_flags_if(CXX_SUPPORTS_${flagname}_FLAG ${flag}) - endforeach() -endmacro() - # Add a list of flags to 'LIBUNWIND_COMPILE_FLAGS'. macro(add_compile_flags) foreach(f ${ARGN}) @@ -202,16 +50,6 @@ macro(add_c_flags_if condition) endif() endmacro() -# For each specified flag, add that flag to 'LIBUNWIND_C_FLAGS' if the -# flag is supported by the C compiler. -macro(add_c_compile_flags_if_supported) - foreach(flag ${ARGN}) - mangle_name("${flag}" flagname) - check_c_compiler_flag("${flag}" "C_SUPPORTS_${flagname}_FLAG") - add_c_flags_if(C_SUPPORTS_${flagname}_FLAG ${flag}) - endforeach() -endmacro() - # Add a list of flags to 'LIBUNWIND_CXX_FLAGS'. macro(add_cxx_flags) foreach(f ${ARGN}) @@ -276,20 +114,3 @@ macro(add_library_flags_if condition) add_library_flags(${ARGN}) endif() endmacro() - -# Turn a comma separated CMake list into a space separated string. -macro(split_list listname) - string(REPLACE ";" " " ${listname} "${${listname}}") -endmacro() - -# For each specified flag, add that compile flag to the provided target. -# The flags are added with the given visibility, i.e. PUBLIC|PRIVATE|INTERFACE. -function(target_add_compile_flags_if_supported target visibility) - foreach(flag ${ARGN}) - mangle_name("${flag}" flagname) - check_cxx_compiler_flag("${flag}" "CXX_SUPPORTS_${flagname}_FLAG") - if (CXX_SUPPORTS_${flagname}_FLAG) - target_compile_options(${target} ${visibility} ${flag}) - endif() - endforeach() -endfunction() diff --git a/src/native/external/llvm-libunwind/cmake/config-ix.cmake b/src/native/external/llvm-libunwind/cmake/config-ix.cmake index d311477f02c69..126c872f0d489 100644 --- a/src/native/external/llvm-libunwind/cmake/config-ix.cmake +++ b/src/native/external/llvm-libunwind/cmake/config-ix.cmake @@ -11,7 +11,11 @@ include(CheckCSourceCompiles) # --unwindlib=none is supported, and use that if possible. llvm_check_compiler_linker_flag(C "--unwindlib=none" CXX_SUPPORTS_UNWINDLIB_EQ_NONE_FLAG) -check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB) +if (HAIKU) + check_library_exists(root fopen "" LIBUNWIND_HAS_ROOT_LIB) +else() + check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB) +endif() if (NOT LIBUNWIND_USE_COMPILER_RT) if (ANDROID) @@ -41,10 +45,15 @@ else() endif() endif() -if (CXX_SUPPORTS_NOSTDLIBXX_FLAG OR C_SUPPORTS_NODEFAULTLIBS_FLAG) +# Only link against compiler-rt manually if we use -nodefaultlibs, since +# otherwise the compiler will do the right thing on its own. +if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG AND C_SUPPORTS_NODEFAULTLIBS_FLAG) if (LIBUNWIND_HAS_C_LIB) list(APPEND CMAKE_REQUIRED_LIBRARIES c) endif () + if (LIBUNWIND_HAS_ROOT_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES root) + endif () if (LIBUNWIND_USE_COMPILER_RT) include(HandleCompilerRT) find_compiler_rt_library(builtins LIBUNWIND_BUILTINS_LIBRARY @@ -71,6 +80,9 @@ if (CXX_SUPPORTS_NOSTDLIBXX_FLAG OR C_SUPPORTS_NODEFAULTLIBS_FLAG) moldname mingwex msvcrt) list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES}) endif() +endif() + +if (CXX_SUPPORTS_NOSTDLIBXX_FLAG OR C_SUPPORTS_NODEFAULTLIBS_FLAG) if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize) set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all") endif () @@ -111,3 +123,7 @@ else() check_library_exists(dl dladdr "" LIBUNWIND_HAS_DL_LIB) check_library_exists(pthread pthread_once "" LIBUNWIND_HAS_PTHREAD_LIB) endif() + +if(HAIKU) + check_library_exists(bsd dl_iterate_phdr "" LIBUNWIND_HAS_BSD_LIB) +endif() diff --git a/src/native/external/llvm-libunwind/docs/conf.py b/src/native/external/llvm-libunwind/docs/conf.py index 404bcb0685759..29f9c24a7ee26 100644 --- a/src/native/external/llvm-libunwind/docs/conf.py +++ b/src/native/external/llvm-libunwind/docs/conf.py @@ -16,106 +16,106 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo'] +extensions = ["sphinx.ext.intersphinx", "sphinx.ext.todo"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'libunwind' -copyright = u'2011-%d, LLVM Project' % date.today().year +project = "libunwind" +copyright = "2011-%d, LLVM Project" % date.today().year # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '16.0' +version = "17.0" # The full version, including alpha/beta/rc tags. -release = '16.0' +release = "17.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -today_fmt = '%Y-%m-%d' +today_fmt = "%Y-%m-%d" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'friendly' +pygments_style = "friendly" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'haiku' +html_theme = "haiku" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -124,101 +124,95 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'libunwinddoc' +htmlhelp_basename = "libunwinddoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('contents', 'libunwind.tex', u'libunwind Documentation', - u'LLVM project', 'manual'), + ("contents", "libunwind.tex", "libunwind Documentation", "LLVM project", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('contents', 'libunwind', u'libunwind Documentation', - [u'LLVM project'], 1) -] +man_pages = [("contents", "libunwind", "libunwind Documentation", ["LLVM project"], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -227,19 +221,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('contents', 'libunwind', u'libunwind Documentation', - u'LLVM project', 'libunwind', 'LLVM Unwinder', - 'Miscellaneous'), + ( + "contents", + "libunwind", + "libunwind Documentation", + "LLVM project", + "libunwind", + "LLVM Unwinder", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # FIXME: Define intersphinx configuration. diff --git a/src/native/external/llvm-libunwind/include/__libunwind_config.h b/src/native/external/llvm-libunwind/include/__libunwind_config.h index b925489e3e7fe..ecfe7be0d12f6 100644 --- a/src/native/external/llvm-libunwind/include/__libunwind_config.h +++ b/src/native/external/llvm-libunwind/include/__libunwind_config.h @@ -36,6 +36,9 @@ # if defined(__linux__) # define _LIBUNWIND_TARGET_LINUX 1 # endif +# if defined(__HAIKU__) +# define _LIBUNWIND_TARGET_HAIKU 1 +# endif # if defined(__i386__) # define _LIBUNWIND_TARGET_I386 # define _LIBUNWIND_CONTEXT_SIZE 13 @@ -201,9 +204,9 @@ # define _LIBUNWIND_TARGET_RISCV 1 # define _LIBUNWIND_TARGET_VE 1 # define _LIBUNWIND_TARGET_S390X 1 -#define _LIBUNWIND_TARGET_LOONGARCH 1 +# define _LIBUNWIND_TARGET_LOONGARCH 1 # define _LIBUNWIND_CONTEXT_SIZE 167 -# define _LIBUNWIND_CURSOR_SIZE 179 +# define _LIBUNWIND_CURSOR_SIZE 204 # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 #endif // _LIBUNWIND_IS_NATIVE_ONLY diff --git a/src/native/external/llvm-libunwind/include/libunwind.h b/src/native/external/llvm-libunwind/include/libunwind.h index 1c5320c1aea8b..7a8fcbcb26745 100644 --- a/src/native/external/llvm-libunwind/include/libunwind.h +++ b/src/native/external/llvm-libunwind/include/libunwind.h @@ -896,6 +896,9 @@ enum { UNW_MIPS_F29 = 61, UNW_MIPS_F30 = 62, UNW_MIPS_F31 = 63, + // HI,LO have been dropped since r6, we keep them here. + // So, when we add DSP/MSA etc, we can use the same register indexes + // for r6 and pre-r6. UNW_MIPS_HI = 64, UNW_MIPS_LO = 65, }; diff --git a/src/native/external/llvm-libunwind/src/AddressSpace.hpp b/src/native/external/llvm-libunwind/src/AddressSpace.hpp index dff3249bf9bfc..33f6441529826 100644 --- a/src/native/external/llvm-libunwind/src/AddressSpace.hpp +++ b/src/native/external/llvm-libunwind/src/AddressSpace.hpp @@ -22,6 +22,7 @@ #include "dwarf2.h" #include "EHHeaderParser.hpp" #include "Registers.hpp" +#include "libunwind_ext.h" #ifndef _LIBUNWIND_USE_DLADDR #if !(defined(_LIBUNWIND_IS_BAREMETAL) || defined(_WIN32) || defined(_AIX)) @@ -66,6 +67,10 @@ char *getFuncNameFromTBTable(uintptr_t pc, uint16_t &NameLen, // In 10.7.0 or later, libSystem.dylib implements this function. extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *); +namespace libunwind { + bool findDynamicUnwindSections(void *, unw_dynamic_unwind_sections *); +} + #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) // When statically linked on bare-metal, the symbols for the EH table are looked @@ -410,8 +415,8 @@ static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base, cbdata->sects->dwarf_index_section = eh_frame_hdr_start; cbdata->sects->dwarf_index_section_length = phdr->p_memsz; if (EHHeaderParser::decodeEHHdr( - *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz, - hdrInfo)) { + *cbdata->addressSpace, eh_frame_hdr_start, + eh_frame_hdr_start + phdr->p_memsz, hdrInfo)) { // .eh_frame_hdr records the start of .eh_frame, but not its size. // Rely on a zero terminator to find the end of the section. cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; @@ -497,6 +502,22 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, info.compact_unwind_section_length = (size_t)dyldInfo.compact_unwind_section_length; return true; } + + unw_dynamic_unwind_sections dynamicUnwindSectionInfo; + if (findDynamicUnwindSections((void *)targetAddr, + &dynamicUnwindSectionInfo)) { + info.dso_base = dynamicUnwindSectionInfo.dso_base; +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + info.dwarf_section = (uintptr_t)dynamicUnwindSectionInfo.dwarf_section; + info.dwarf_section_length = dynamicUnwindSectionInfo.dwarf_section_length; +#endif + info.compact_unwind_section = + (uintptr_t)dynamicUnwindSectionInfo.compact_unwind_section; + info.compact_unwind_section_length = + dynamicUnwindSectionInfo.compact_unwind_section_length; + return true; + } + #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) info.dso_base = 0; // Bare metal is statically linked, so no need to ask the dynamic loader @@ -614,7 +635,8 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, info.dwarf_index_section_length = SIZE_MAX; EHHeaderParser::EHHeaderInfo hdrInfo; if (!EHHeaderParser::decodeEHHdr( - *this, info.dwarf_index_section, info.dwarf_index_section_length, + *this, info.dwarf_index_section, + info.dwarf_index_section + info.dwarf_index_section_length, hdrInfo)) { return false; } diff --git a/src/native/external/llvm-libunwind/src/CMakeLists.txt b/src/native/external/llvm-libunwind/src/CMakeLists.txt index df32e53d69e6e..780430ba70ba6 100644 --- a/src/native/external/llvm-libunwind/src/CMakeLists.txt +++ b/src/native/external/llvm-libunwind/src/CMakeLists.txt @@ -5,11 +5,6 @@ set(LIBUNWIND_CXX_SOURCES Unwind-EHABI.cpp Unwind-seh.cpp ) -if(APPLE) - list(APPEND LIBUNWIND_CXX_SOURCES - Unwind_AppleExtras.cpp - ) -endif() if(${CMAKE_SYSTEM_NAME} MATCHES "AIX") list(APPEND LIBUNWIND_CXX_SOURCES @@ -21,6 +16,7 @@ set(LIBUNWIND_C_SOURCES UnwindLevel1.c UnwindLevel1-gcc-ext.c Unwind-sjlj.c + Unwind-wasm.c ) set_source_files_properties(${LIBUNWIND_C_SOURCES} PROPERTIES @@ -31,15 +27,6 @@ set(LIBUNWIND_ASM_SOURCES UnwindRegistersSave.S ) -# See add_asm_sources() in compiler-rt for explanation of this workaround. -# CMake doesn't work correctly with assembly on AIX. Workaround by compiling -# as C files as well. -if((APPLE AND CMAKE_VERSION VERSION_LESS 3.19) OR - (MINGW AND CMAKE_VERSION VERSION_LESS 3.17) OR - (${CMAKE_SYSTEM_NAME} MATCHES "AIX")) - set_source_files_properties(${LIBUNWIND_ASM_SOURCES} PROPERTIES LANGUAGE C) -endif() - set(LIBUNWIND_HEADERS AddressSpace.hpp assembly.h @@ -79,26 +66,32 @@ set(LIBUNWIND_SOURCES ${LIBUNWIND_ASM_SOURCES}) # Generate library list. -add_library_flags_if(LIBUNWIND_HAS_C_LIB c) -if (LIBUNWIND_USE_COMPILER_RT) - add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") +if (CXX_SUPPORTS_NOSTDLIBXX_FLAG) + add_link_flags_if_supported(-nostdlib++) else() - add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s) - add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc) + if (LIBUNWIND_USE_COMPILER_RT) + add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") + else() + add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s) + add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc) + endif() + add_library_flags_if(LIBUNWIND_HAS_C_LIB c) +endif() + +if (NOT APPLE) + add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl) +endif() + +if (LIBUNWIND_ENABLE_THREADS AND NOT APPLE) + add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) endif() -add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl) + if (LIBUNWIND_ENABLE_THREADS) - add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) endif() # Setup flags. add_link_flags_if(CXX_SUPPORTS_UNWINDLIB_EQ_NONE_FLAG --unwindlib=none) -if (CXX_SUPPORTS_NOSTDLIBXX_FLAG) - add_link_flags_if_supported(-nostdlib++) -else() - add_link_flags_if_supported(-nodefaultlibs) -endif() # MINGW_LIBRARIES is defined in config-ix.cmake add_library_flags_if(MINGW "${MINGW_LIBRARIES}") @@ -121,6 +114,16 @@ if (APPLE) endif () endif () +if (HAIKU) + add_library_flags_if(LIBUNWIND_HAS_ROOT_LIB root) + + add_library_flags_if(LIBUNWIND_HAS_BSD_LIB bsd) + add_compile_flags_if(LIBUNWIND_HAS_BSD_LIB -D_LIBUNWIND_USE_HAIKU_BSD_LIB=1) + + add_compile_flags("-D_DEFAULT_SOURCE") + add_compile_flags("-DPT_GNU_EH_FRAME=PT_EH_FRAME") +endif () + string(REPLACE ";" " " LIBUNWIND_COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}") string(REPLACE ";" " " LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}") string(REPLACE ";" " " LIBUNWIND_C_FLAGS "${LIBUNWIND_C_FLAGS}") @@ -135,18 +138,23 @@ set_property(SOURCE ${LIBUNWIND_C_SOURCES} # ease, but does not rely on C++ at runtime. set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") +include(WarningFlags) + # Build the shared library. add_library(unwind_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) +cxx_add_warning_flags(unwind_shared_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC}) if(CMAKE_C_COMPILER_ID STREQUAL MSVC) target_compile_options(unwind_shared_objects PRIVATE /GR-) else() target_compile_options(unwind_shared_objects PRIVATE -fno-rtti) endif() target_link_libraries(unwind_shared_objects PRIVATE unwind-headers ${LIBUNWIND_LIBRARIES}) +target_compile_options(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}") +target_link_libraries(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}") set_target_properties(unwind_shared_objects PROPERTIES CXX_EXTENSIONS OFF - CXX_STANDARD 11 + CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" ) @@ -174,23 +182,29 @@ endif() # Build the static library. add_library(unwind_static_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) +cxx_add_warning_flags(unwind_static_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC}) if(CMAKE_C_COMPILER_ID STREQUAL MSVC) target_compile_options(unwind_static_objects PRIVATE /GR-) else() target_compile_options(unwind_static_objects PRIVATE -fno-rtti) endif() target_link_libraries(unwind_static_objects PRIVATE unwind-headers ${LIBUNWIND_LIBRARIES}) +target_compile_options(unwind_static_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}") +target_link_libraries(unwind_static_objects PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}") set_target_properties(unwind_static_objects PROPERTIES CXX_EXTENSIONS OFF - CXX_STANDARD 11 + CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" ) if(LIBUNWIND_HIDE_SYMBOLS) target_add_compile_flags_if_supported(unwind_static_objects PRIVATE -fvisibility=hidden) - target_add_compile_flags_if_supported(unwind_static_objects PRIVATE -fvisibility-global-new-delete-hidden) + target_add_compile_flags_if_supported(unwind_static_objects PRIVATE -fvisibility-global-new-delete=force-hidden) + if (NOT CXX_SUPPORTS_FVISIBILITY_GLOBAL_NEW_DELETE_EQ_FORCE_HIDDEN_FLAG) + target_add_compile_flags_if_supported(unwind_static_objects PRIVATE -fvisibility-global-new-delete-hidden) + endif() target_compile_definitions(unwind_static_objects PRIVATE _LIBUNWIND_HIDE_SYMBOLS) endif() diff --git a/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp b/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp index d242a549e5b47..a4e75755e5b4d 100644 --- a/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp +++ b/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp @@ -69,7 +69,7 @@ class DwarfInstructions { return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + prolog.cfaRegisterOffset); if (prolog.cfaExpression != 0) - return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); assert(0 && "getCFA(): unknown location"); __builtin_unreachable(); diff --git a/src/native/external/llvm-libunwind/src/EHHeaderParser.hpp b/src/native/external/llvm-libunwind/src/EHHeaderParser.hpp index ed4317c89055c..0662a1321e2c7 100644 --- a/src/native/external/llvm-libunwind/src/EHHeaderParser.hpp +++ b/src/native/external/llvm-libunwind/src/EHHeaderParser.hpp @@ -55,6 +55,19 @@ template bool EHHeaderParser::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) { pint_t p = ehHdrStart; + + // Ensure that we don't read data beyond the end of .eh_frame_hdr + if (ehHdrEnd - ehHdrStart < 4) { + // Don't print a message for an empty .eh_frame_hdr (this can happen if + // the linker script defines symbols for it even in the empty case). + if (ehHdrEnd == ehHdrStart) + return false; + _LIBUNWIND_LOG("unsupported .eh_frame_hdr at %" PRIx64 + ": need at least 4 bytes of data but only got %zd", + static_cast(ehHdrStart), + static_cast(ehHdrEnd - ehHdrStart)); + return false; + } uint8_t version = addressSpace.get8(p++); if (version != 1) { _LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64, diff --git a/src/native/external/llvm-libunwind/src/FrameHeaderCache.hpp b/src/native/external/llvm-libunwind/src/FrameHeaderCache.hpp index 54d5d33c3cd7e..296064d8e2e67 100644 --- a/src/native/external/llvm-libunwind/src/FrameHeaderCache.hpp +++ b/src/native/external/llvm-libunwind/src/FrameHeaderCache.hpp @@ -31,8 +31,8 @@ class _LIBUNWIND_HIDDEN FrameHeaderCache { struct CacheEntry { - uintptr_t LowPC() { return Info.dso_base; }; - uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }; + uintptr_t LowPC() { return Info.dso_base; } + uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; } UnwindInfoSections Info; CacheEntry *Next; }; @@ -41,7 +41,7 @@ class _LIBUNWIND_HIDDEN FrameHeaderCache { // Can't depend on the C++ standard library in libunwind, so use an array to // allocate the entries, and two linked lists for ordering unused and recently - // used entries. FIXME: Would the the extra memory for a doubly-linked list + // used entries. FIXME: Would the extra memory for a doubly-linked list // be better than the runtime cost of traversing a very short singly-linked // list on a cache miss? The entries themselves are all small and consecutive, // so unlikely to cause page faults when following the pointers. The memory diff --git a/src/native/external/llvm-libunwind/src/Registers.hpp b/src/native/external/llvm-libunwind/src/Registers.hpp index 4a0c8d83bb40c..4e1d75519ef35 100644 --- a/src/native/external/llvm-libunwind/src/Registers.hpp +++ b/src/native/external/llvm-libunwind/src/Registers.hpp @@ -753,6 +753,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc { void setIP(uint32_t value) { _registers.__srr0 = value; } uint64_t getCR() const { return _registers.__cr; } void setCR(uint32_t value) { _registers.__cr = value; } + uint64_t getLR() const { return _registers.__lr; } + void setLR(uint32_t value) { _registers.__lr = value; } private: struct ppc_thread_state_t { @@ -1324,6 +1326,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc64 { void setIP(uint64_t value) { _registers.__srr0 = value; } uint64_t getCR() const { return _registers.__cr; } void setCR(uint64_t value) { _registers.__cr = value; } + uint64_t getLR() const { return _registers.__lr; } + void setLR(uint64_t value) { _registers.__lr = value; } private: struct ppc64_thread_state_t { @@ -3070,7 +3074,7 @@ inline bool Registers_mips_o32::validRegister(int regNum) const { return false; if (regNum <= UNW_MIPS_R31) return true; -#if __mips_isa_rev != 6 +#if __mips_isa_rev < 6 if (regNum == UNW_MIPS_HI) return true; if (regNum == UNW_MIPS_LO) @@ -3104,10 +3108,12 @@ inline uint32_t Registers_mips_o32::getRegister(int regNum) const { return _registers.__pc; case UNW_REG_SP: return _registers.__r[29]; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: return _registers.__hi; case UNW_MIPS_LO: return _registers.__lo; +#endif } _LIBUNWIND_ABORT("unsupported mips_o32 register"); } @@ -3137,11 +3143,13 @@ inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) { case UNW_REG_SP: _registers.__r[29] = value; return; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: _registers.__hi = value; return; case UNW_MIPS_LO: _registers.__lo = value; +#endif return; } _LIBUNWIND_ABORT("unsupported mips_o32 register"); @@ -3321,10 +3329,12 @@ inline const char *Registers_mips_o32::getRegisterName(int regNum) { return "$f30"; case UNW_MIPS_F31: return "$f31"; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: return "$hi"; case UNW_MIPS_LO: return "$lo"; +#endif default: return "unknown register"; } @@ -3394,7 +3404,7 @@ inline bool Registers_mips_newabi::validRegister(int regNum) const { return false; if (regNum <= UNW_MIPS_R31) return true; -#if __mips_isa_rev != 6 +#if __mips_isa_rev < 6 if (regNum == UNW_MIPS_HI) return true; if (regNum == UNW_MIPS_LO) @@ -3413,10 +3423,12 @@ inline uint64_t Registers_mips_newabi::getRegister(int regNum) const { return _registers.__pc; case UNW_REG_SP: return _registers.__r[29]; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: return _registers.__hi; case UNW_MIPS_LO: return _registers.__lo; +#endif } _LIBUNWIND_ABORT("unsupported mips_newabi register"); } @@ -3434,12 +3446,14 @@ inline void Registers_mips_newabi::setRegister(int regNum, uint64_t value) { case UNW_REG_SP: _registers.__r[29] = value; return; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: _registers.__hi = value; return; case UNW_MIPS_LO: _registers.__lo = value; return; +#endif } _LIBUNWIND_ABORT("unsupported mips_newabi register"); } @@ -3618,10 +3632,12 @@ inline const char *Registers_mips_newabi::getRegisterName(int regNum) { return "$f30"; case UNW_MIPS_F31: return "$f31"; +#if __mips_isa_rev < 6 case UNW_MIPS_HI: return "$hi"; case UNW_MIPS_LO: return "$lo"; +#endif default: return "unknown register"; } diff --git a/src/native/external/llvm-libunwind/src/Unwind-EHABI.cpp b/src/native/external/llvm-libunwind/src/Unwind-EHABI.cpp index 63d01aaf9cef4..338c853bca780 100644 --- a/src/native/external/llvm-libunwind/src/Unwind-EHABI.cpp +++ b/src/native/external/llvm-libunwind/src/Unwind-EHABI.cpp @@ -709,7 +709,7 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, // Update info about this frame. unw_proc_info_t frameInfo; if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { - _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info " "failed => _URC_END_OF_STACK", (void *)exception_object); return _URC_FATAL_PHASE2_ERROR; @@ -885,8 +885,11 @@ _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { return result; } -static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, - void* valuep) { +// Only used in _LIBUNWIND_TRACE_API, which is a no-op when assertions are +// disabled. +[[gnu::unused]] static uint64_t +ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, + const void *valuep) { uint64_t value = 0; switch (representation) { case _UVRSD_UINT32: diff --git a/src/native/external/llvm-libunwind/src/Unwind-seh.cpp b/src/native/external/llvm-libunwind/src/Unwind-seh.cpp index d08c29c21383b..b2bb119ed6d29 100644 --- a/src/native/external/llvm-libunwind/src/Unwind-seh.cpp +++ b/src/native/external/llvm-libunwind/src/Unwind-seh.cpp @@ -212,11 +212,20 @@ __libunwind_seh_personality(int version, _Unwind_Action state, ms_exc.ExceptionInformation[2] = state; DISPATCHER_CONTEXT *disp_ctx = __unw_seh_get_disp_ctx((unw_cursor_t *)context); + _LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() calling " + "LanguageHandler %p(%p, %p, %p, %p)", + (void *)disp_ctx->LanguageHandler, (void *)&ms_exc, + (void *)disp_ctx->EstablisherFrame, + (void *)disp_ctx->ContextRecord, (void *)disp_ctx); EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc, (PVOID)disp_ctx->EstablisherFrame, disp_ctx->ContextRecord, disp_ctx); + _LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() LanguageHandler " + "returned %d", + (int)ms_act); switch (ms_act) { + case ExceptionContinueExecution: return _URC_END_OF_STACK; case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND; case 4 /*ExceptionExecuteHandler*/: return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND; @@ -238,7 +247,7 @@ unwind_phase2_forced(unw_context_t *uc, // Update info about this frame. unw_proc_info_t frameInfo; if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { - _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info " "failed => _URC_END_OF_STACK", (void *)exception_object); return _URC_FATAL_PHASE2_ERROR; @@ -304,6 +313,12 @@ unwind_phase2_forced(unw_context_t *uc, // We may get control back if landing pad calls _Unwind_Resume(). __unw_resume(&cursor2); break; + case _URC_END_OF_STACK: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_END_OF_STACK", + (void *)exception_object); + break; default: // Personality routine returned an unknown result code. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " @@ -312,6 +327,8 @@ unwind_phase2_forced(unw_context_t *uc, (void *)exception_object, personalityResult); return _URC_FATAL_PHASE2_ERROR; } + if (personalityResult == _URC_END_OF_STACK) + break; } } diff --git a/src/native/external/llvm-libunwind/src/Unwind-sjlj.c b/src/native/external/llvm-libunwind/src/Unwind-sjlj.c index 90a55fd29db1f..7e8faf098fe14 100644 --- a/src/native/external/llvm-libunwind/src/Unwind-sjlj.c +++ b/src/native/external/llvm-libunwind/src/Unwind-sjlj.c @@ -82,7 +82,8 @@ struct _Unwind_FunctionContext { static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL; #endif -static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() { +static struct _Unwind_FunctionContext * +__Unwind_SjLj_GetTopOfFunctionStack(void) { #if defined(__APPLE__) return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key); #else @@ -426,7 +427,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, /// Called by personality handler during phase 2 to alter register values. _LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, uintptr_t new_value) { - _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIuPTR + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIxPTR ")", (void *)context, index, new_value); _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; @@ -437,7 +438,7 @@ _LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, /// Called by personality handler during phase 2 to get instruction pointer. _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; - _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIu32, + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR, (void *)context, ufc->resumeLocation + 1); return ufc->resumeLocation + 1; } @@ -450,7 +451,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, int *ipBefore) { _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; *ipBefore = 0; - _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIu32, + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIxPTR, (void *)context, (void *)ipBefore, ufc->resumeLocation + 1); return ufc->resumeLocation + 1; @@ -460,7 +461,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, /// Called by personality handler during phase 2 to alter instruction pointer. _LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t new_value) { - _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIuPTR ")", + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIxPTR ")", (void *)context, new_value); _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; ufc->resumeLocation = new_value - 1; diff --git a/src/native/external/llvm-libunwind/src/Unwind-wasm.c b/src/native/external/llvm-libunwind/src/Unwind-wasm.c new file mode 100644 index 0000000000000..f7f39d38b59c1 --- /dev/null +++ b/src/native/external/llvm-libunwind/src/Unwind-wasm.c @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements Wasm exception handling proposal +// (https://github.com/WebAssembly/exception-handling) based C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include + +#include "config.h" + +#ifdef __USING_WASM_EXCEPTIONS__ + +#include "unwind.h" +#include + +_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception *unwind_exception, + _Unwind_Context *context); + +struct _Unwind_LandingPadContext { + // Input information to personality function + uintptr_t lpad_index; // landing pad index + uintptr_t lsda; // LSDA address + + // Output information computed by personality function + uintptr_t selector; // selector value +}; + +// Communication channel between compiler-generated user code and personality +// function +thread_local struct _Unwind_LandingPadContext __wasm_lpad_context; + +/// Calls to this function is in landing pads in compiler-generated user code. +/// In other EH schemes, stack unwinding is done by libunwind library, which +/// calls the personality function for each each frame it lands. On the other +/// hand, WebAssembly stack unwinding process is performed by a VM, and the +/// personality function cannot be called from there. So the compiler inserts +/// a call to this function in landing pads in the user code, which in turn +/// calls the personality function. +_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) { + struct _Unwind_Exception *exception_object = + (struct _Unwind_Exception *)exception_ptr; + _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)", + (void *)exception_object); + + // Reset the selector. + __wasm_lpad_context.selector = 0; + + // Call personality function. Wasm does not have two-phase unwinding, so we + // only do the cleanup phase. + return __gxx_personality_wasm0( + 1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)&__wasm_lpad_context); +} + +/// Called by __cxa_throw. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(exception_object=%p)", + (void *)exception_object); + // Use Wasm EH's 'throw' instruction. + __builtin_wasm_throw(0, exception_object); +} + +/// Called by __cxa_end_catch. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + (void *)(exception_object)); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +/// Called by personality handler to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, index=%d, value=%lu)", + (void *)context, index, value); + // We only use this function to set __wasm_lpad_context.selector field, which + // is index 1 in the personality function. + if (index == 1) + ((struct _Unwind_LandingPadContext *)context)->selector = value; +} + +/// Called by personality handler to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + // The result will be used as an 1-based index after decrementing 1, so we + // increment 2 here + uintptr_t result = + ((struct _Unwind_LandingPadContext *)context)->lpad_index + 2; + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => %lu", (void *)context, + result); + return result; +} + +/// Not used in Wasm. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t value) {} + +/// Called by personality handler to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + uintptr_t result = ((struct _Unwind_LandingPadContext *)context)->lsda; + _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lx", + (void *)context, result); + return result; +} + +/// Not used in Wasm. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + return 0; +} + +#endif // defined(__USING_WASM_EXCEPTIONS__) diff --git a/src/native/external/llvm-libunwind/src/UnwindCursor.hpp b/src/native/external/llvm-libunwind/src/UnwindCursor.hpp index 67f4d16ef14d3..606ba0b0a8d31 100644 --- a/src/native/external/llvm-libunwind/src/UnwindCursor.hpp +++ b/src/native/external/llvm-libunwind/src/UnwindCursor.hpp @@ -31,7 +31,10 @@ #endif #if defined(_LIBUNWIND_TARGET_LINUX) && \ - (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X)) + (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ + defined(_LIBUNWIND_TARGET_S390X)) +#include +#include #include #include #include @@ -509,7 +512,14 @@ class UnwindCursor : public AbstractUnwindCursor { #endif DISPATCHER_CONTEXT *getDispatcherContext() { return &_dispContext; } - void setDispatcherContext(DISPATCHER_CONTEXT *disp) { _dispContext = *disp; } + void setDispatcherContext(DISPATCHER_CONTEXT *disp) { + _dispContext = *disp; + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (_dispContext.LanguageHandler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + } // libunwind does not and should not depend on C++ library which means that we // need our own definition of inline placement new. @@ -571,10 +581,12 @@ UnwindCursor::UnwindCursor(unw_context_t *context, A &as) "UnwindCursor<> requires more alignment than unw_cursor_t"); memset(&_info, 0, sizeof(_info)); memset(&_histTable, 0, sizeof(_histTable)); + memset(&_dispContext, 0, sizeof(_dispContext)); _dispContext.ContextRecord = &_msContext; _dispContext.HistoryTable = &_histTable; // Initialize MS context from ours. R r(context); + RtlCaptureContext(&_msContext); _msContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT; #if defined(_LIBUNWIND_TARGET_X86_64) _msContext.Rax = r.getRegister(UNW_X86_64_RAX); @@ -672,6 +684,7 @@ UnwindCursor::UnwindCursor(CONTEXT *context, A &as) "UnwindCursor<> does not fit in unw_cursor_t"); memset(&_info, 0, sizeof(_info)); memset(&_histTable, 0, sizeof(_histTable)); + memset(&_dispContext, 0, sizeof(_dispContext)); _dispContext.ContextRecord = &_msContext; _dispContext.HistoryTable = &_histTable; _msContext = *context; @@ -682,7 +695,7 @@ template bool UnwindCursor::validReg(int regNum) { if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) return true; #if defined(_LIBUNWIND_TARGET_X86_64) - if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true; + if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_RIP) return true; #elif defined(_LIBUNWIND_TARGET_ARM) if ((regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) || regNum == UNW_ARM_RA_AUTH_CODE) @@ -697,6 +710,7 @@ template unw_word_t UnwindCursor::getReg(int regNum) { switch (regNum) { #if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_X86_64_RIP: case UNW_REG_IP: return _msContext.Rip; case UNW_X86_64_RAX: return _msContext.Rax; case UNW_X86_64_RDX: return _msContext.Rdx; @@ -747,6 +761,7 @@ template void UnwindCursor::setReg(int regNum, unw_word_t value) { switch (regNum) { #if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_X86_64_RIP: case UNW_REG_IP: _msContext.Rip = value; break; case UNW_X86_64_RAX: _msContext.Rax = value; break; case UNW_X86_64_RDX: _msContext.Rdx = value; break; @@ -983,10 +998,15 @@ class UnwindCursor : public AbstractUnwindCursor{ R dummy; return stepThroughSigReturn(dummy); } + bool isReadableAddr(const pint_t addr) const; #if defined(_LIBUNWIND_TARGET_AARCH64) bool setInfoForSigReturn(Registers_arm64 &); int stepThroughSigReturn(Registers_arm64 &); #endif +#if defined(_LIBUNWIND_TARGET_RISCV) + bool setInfoForSigReturn(Registers_riscv &); + int stepThroughSigReturn(Registers_riscv &); +#endif #if defined(_LIBUNWIND_TARGET_S390X) bool setInfoForSigReturn(Registers_s390x &); int stepThroughSigReturn(Registers_s390x &); @@ -2003,6 +2023,9 @@ bool UnwindCursor::getInfoFromSEH(pint_t pc) { uint32_t lastcode = (xdata->CountOfCodes + 1) & ~1; const uint32_t *handler = reinterpret_cast(&xdata->UnwindCodes[lastcode]); _info.lsda = reinterpret_cast(handler+1); + _dispContext.HandlerData = reinterpret_cast(_info.lsda); + _dispContext.LanguageHandler = + reinterpret_cast(base + *handler); if (*handler) { _info.handler = reinterpret_cast(__libunwind_seh_personality); } else @@ -2306,27 +2329,39 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) { functionName = ".anonymous."; } - _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p", - __func__, functionName, - reinterpret_cast(TBTable)); + _LIBUNWIND_TRACE_UNWINDING( + "%s: Look up traceback table of func=%s at %p, pc=%p, " + "SP=%p, saves_lr=%d, stores_bc=%d", + __func__, functionName, reinterpret_cast(TBTable), + reinterpret_cast(pc), + reinterpret_cast(registers.getSP()), TBTable->tb.saves_lr, + TBTable->tb.stores_bc); } #if defined(__powerpc64__) - // Instruction to reload TOC register "l r2,40(r1)" + // Instruction to reload TOC register "ld r2,40(r1)" const uint32_t loadTOCRegInst = 0xe8410028; const int32_t unwPPCF0Index = UNW_PPC64_F0; const int32_t unwPPCV0Index = UNW_PPC64_V0; #else - // Instruction to reload TOC register "l r2,20(r1)" + // Instruction to reload TOC register "lwz r2,20(r1)" const uint32_t loadTOCRegInst = 0x80410014; const int32_t unwPPCF0Index = UNW_PPC_F0; const int32_t unwPPCV0Index = UNW_PPC_V0; #endif + // lastStack points to the stack frame of the next routine up. + pint_t curStack = static_cast(registers.getSP()); + pint_t lastStack = *reinterpret_cast(curStack); + + if (lastStack == 0) + return UNW_STEP_END; + R newRegisters = registers; - // lastStack points to the stack frame of the next routine up. - pint_t lastStack = *(reinterpret_cast(registers.getSP())); + // If backchain is not stored, use the current stack frame. + if (!TBTable->tb.stores_bc) + lastStack = curStack; // Return address is the address after call site instruction. pint_t returnAddress; @@ -2336,33 +2371,41 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, reinterpret_cast(lastStack)); sigcontext *sigContext = reinterpret_cast( - reinterpret_cast(lastStack) + STKMIN); + reinterpret_cast(lastStack) + STKMINALIGN); returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; - _LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n", - reinterpret_cast(sigContext), - reinterpret_cast(returnAddress)); - + bool useSTKMIN = false; if (returnAddress < 0x10000000) { - // Try again using STKMINALIGN + // Try again using STKMIN. sigContext = reinterpret_cast( - reinterpret_cast(lastStack) + STKMINALIGN); + reinterpret_cast(lastStack) + STKMIN); returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; if (returnAddress < 0x10000000) { - _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n", - reinterpret_cast(returnAddress)); + _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p from sigcontext=%p", + reinterpret_cast(returnAddress), + reinterpret_cast(sigContext)); return UNW_EBADFRAME; - } else { - _LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: " - "sigContext=%p, returnAddress=%p. " - "Seems to be a valid address\n", - reinterpret_cast(sigContext), - reinterpret_cast(returnAddress)); } + useSTKMIN = true; } + _LIBUNWIND_TRACE_UNWINDING("Returning from a signal handler %s: " + "sigContext=%p, returnAddress=%p. " + "Seems to be a valid address", + useSTKMIN ? "STKMIN" : "STKMINALIGN", + reinterpret_cast(sigContext), + reinterpret_cast(returnAddress)); + // Restore the condition register from sigcontext. newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr); + // Save the LR in sigcontext for stepping up when the function that + // raised the signal is a leaf function. This LR has the return address + // to the caller of the leaf function. + newRegisters.setLR(sigContext->sc_jmpbuf.jmp_context.lr); + _LIBUNWIND_TRACE_UNWINDING( + "Save LR=%p from sigcontext", + reinterpret_cast(sigContext->sc_jmpbuf.jmp_context.lr)); + // Restore GPRs from sigcontext. for (int i = 0; i < 32; ++i) newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]); @@ -2385,13 +2428,26 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, } } else { // Step up a normal frame. - returnAddress = reinterpret_cast(lastStack)[2]; - _LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, " - "returnAddress=%p\n", - reinterpret_cast(lastStack), - reinterpret_cast(returnAddress)); - _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n", + if (!TBTable->tb.saves_lr && registers.getLR()) { + // This case should only occur if we were called from a signal handler + // and the signal occurred in a function that doesn't save the LR. + returnAddress = static_cast(registers.getLR()); + _LIBUNWIND_TRACE_UNWINDING("Use saved LR=%p", + reinterpret_cast(returnAddress)); + } else { + // Otherwise, use the LR value in the stack link area. + returnAddress = reinterpret_cast(lastStack)[2]; + } + + // Reset LR in the current context. + newRegisters.setLR(NULL); + + _LIBUNWIND_TRACE_UNWINDING( + "Extract info from lastStack=%p, returnAddress=%p", + reinterpret_cast(lastStack), + reinterpret_cast(returnAddress)); + _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d", TBTable->tb.fpr_saved, TBTable->tb.gpr_saved, TBTable->tb.saves_cr); @@ -2455,7 +2511,7 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, struct vec_ext *vec_ext = reinterpret_cast(charPtr); - _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved); + _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d", vec_ext->vr_saved); // Restore vector register(s) if saved on the stack. if (vec_ext->vr_saved) { @@ -2485,11 +2541,11 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, // Do we need to set the TOC register? _LIBUNWIND_TRACE_UNWINDING( - "Current gpr2=%p\n", + "Current gpr2=%p", reinterpret_cast(newRegisters.getRegister(2))); if (firstInstruction == loadTOCRegInst) { _LIBUNWIND_TRACE_UNWINDING( - "Set gpr2=%p from frame\n", + "Set gpr2=%p from frame", reinterpret_cast(reinterpret_cast(lastStack)[5])); newRegisters.setRegister(2, reinterpret_cast(lastStack)[5]); } @@ -2521,7 +2577,6 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, } else { isSignalFrame = false; } - return UNW_STEP_SUCCESS; } #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) @@ -2673,20 +2728,12 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. - uint32_t instructions[2]; - struct iovec local_iov = {&instructions, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc)) + return false; + auto *instructions = reinterpret_cast(pc); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || - instructions[1] != 0xd4000001) + if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001) return false; _info = {}; @@ -2730,6 +2777,59 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && // defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_RISCV) +template +bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { + const pint_t pc = static_cast(getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc)) + return false; + const auto *instructions = reinterpret_cast(pc); + // Look for the two instructions used in the sigreturn trampoline + // __vdso_rt_sigreturn: + // + // 0x08b00893 li a7,0x8b + // 0x00000073 ecall + if (instructions[0] != 0x08b00893 || instructions[1] != 0x00000073) + return false; + + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 4; + _isSigReturn = true; + return true; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_riscv &) { + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext_t struct: + // - 8-byte long (__uc_flags) + // - 8-byte pointer (*uc_link) + // - 24-byte uc_stack + // - 8-byte uc_sigmask + // - 120-byte of padding to allow sigset_t to be expanded in the future + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - struct sigcontext uc_mcontext + // [1] + // https://github.com/torvalds/linux/blob/master/arch/riscv/kernel/signal.c + const pint_t kOffsetSpToSigcontext = 128 + 8 + 8 + 24 + 8 + 128; + + const pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + _registers.setIP(_addressSpace.get64(sigctx)); + for (int i = UNW_RISCV_X1; i <= UNW_RISCV_X31; ++i) { + uint64_t value = _addressSpace.get64(sigctx + static_cast(i * 8)); + _registers.setRegister(i, value); + } + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_RISCV) + #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ defined(_LIBUNWIND_TARGET_S390X) template @@ -2741,13 +2841,11 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to - // read the memory safely instead. - uint16_t inst; - struct iovec local_iov = {&inst, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0); - if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) { + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc)) + return false; + const auto inst = *reinterpret_cast(pc); + if (inst == 0x0a77 || inst == 0x0aad) { _info = {}; _info.start_ip = pc; _info.end_ip = pc + 2; @@ -2893,6 +2991,37 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // We use SYS_rt_sigprocmask, inspired by Abseil's AddressIsReadable. + + const auto sigsetAddr = reinterpret_cast(addr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) + return false; + const auto saveErrno = errno; + // We MUST use a raw syscall here, as wrappers may try to access + // sigsetAddr which may cause a SIGSEGV. A raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). For the majority of architectures, + // it's 64 bits (_NSIG), and libc NSIG is _NSIG + 1. + const auto kernelSigsetSize = NSIG / 8; + [[maybe_unused]] const int Result = syscall( + SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, nullptr, kernelSigsetSize); + // Because our "how" is invalid, this syscall should always fail, and our + // errno should always be EINVAL or an EFAULT. This relies on the Linux + // kernel to check copy_from_user before checking if the "how" argument is + // invalid. + assert(Result == -1); + assert(errno == EFAULT || errno == EINVAL); + const auto readable = errno != EFAULT; + errno = saveErrno; + return readable; +} +#endif + #if defined(_LIBUNWIND_USE_CET) extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) { AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; diff --git a/src/native/external/llvm-libunwind/src/UnwindLevel1-gcc-ext.c b/src/native/external/llvm-libunwind/src/UnwindLevel1-gcc-ext.c index efb872bbe59eb..32c872ffade1f 100644 --- a/src/native/external/llvm-libunwind/src/UnwindLevel1-gcc-ext.c +++ b/src/native/external/llvm-libunwind/src/UnwindLevel1-gcc-ext.c @@ -143,7 +143,7 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { // Create a mock exception object for force unwinding. _Unwind_Exception ex; memset(&ex, '\0', sizeof(ex)); - strcpy((char *)&ex.exception_class, "CLNGUNW"); + memcpy(&ex.exception_class, "CLNGUNW", sizeof(ex.exception_class)); #endif // walk each frame @@ -167,7 +167,7 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { } // Update the pr_cache in the mock exception object. - const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; + uint32_t *unwindInfo = (uint32_t *)frameInfo.unwind_info; ex.pr_cache.fnstart = frameInfo.start_ip; ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; ex.pr_cache.additional= frameInfo.flags; diff --git a/src/native/external/llvm-libunwind/src/UnwindLevel1.c b/src/native/external/llvm-libunwind/src/UnwindLevel1.c index 7e9adf64246df..05d0f2cb0a0a7 100644 --- a/src/native/external/llvm-libunwind/src/UnwindLevel1.c +++ b/src/native/external/llvm-libunwind/src/UnwindLevel1.c @@ -321,7 +321,7 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, unw_proc_info_t frameInfo; if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING( - "unwind_phase2_forced(ex_obj=%p): __unw_step_stage2 " + "unwind_phase2_forced(ex_obj=%p): __unw_get_proc_info " "failed => _URC_END_OF_STACK", (void *)exception_object); return _URC_FATAL_PHASE2_ERROR; diff --git a/src/native/external/llvm-libunwind/src/UnwindRegistersRestore.S b/src/native/external/llvm-libunwind/src/UnwindRegistersRestore.S index 5d9607ad798dd..42c2488fc7cf7 100644 --- a/src/native/external/llvm-libunwind/src/UnwindRegistersRestore.S +++ b/src/native/external/llvm-libunwind/src/UnwindRegistersRestore.S @@ -645,7 +645,8 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp x10,x11, [x0, #0x050] ldp x12,x13, [x0, #0x060] ldp x14,x15, [x0, #0x070] - ldp x16,x17, [x0, #0x080] + // x16 and x17 were clobbered by the call into the unwinder, so no point in + // restoring them. ldp x18,x19, [x0, #0x090] ldp x20,x21, [x0, #0x0A0] ldp x22,x23, [x0, #0x0B0] @@ -653,8 +654,6 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp x26,x27, [x0, #0x0D0] ldp x28,x29, [x0, #0x0E0] ldr x30, [x0, #0x100] // restore pc into lr - ldr x1, [x0, #0x0F8] - mov sp,x1 // restore sp ldp d0, d1, [x0, #0x110] ldp d2, d3, [x0, #0x120] @@ -674,7 +673,13 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldr d30, [x0, #0x200] ldr d31, [x0, #0x208] + // Finally, restore sp. This must be done after the last read from the + // context struct, because it is allocated on the stack, and an exception + // could clobber the de-allocated portion of the stack after sp has been + // restored. + ldr x16, [x0, #0x0F8] ldp x0, x1, [x0, #0x000] // restore x0,x1 + mov sp,x16 // restore sp ret x30 // jump to pc #elif defined(__arm__) && !defined(__APPLE__) @@ -988,11 +993,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv) ldc1 $f31, (4 * 36 + 8 * 31)($4) #endif #endif +#if __mips_isa_rev < 6 // restore hi and lo lw $8, (4 * 33)($4) mthi $8 lw $8, (4 * 34)($4) mtlo $8 +#endif // r0 is zero lw $1, (4 * 1)($4) lw $2, (4 * 2)($4) @@ -1049,11 +1056,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) ldc1 $f\i, (280+8*\i)($4) .endr #endif +#if __mips_isa_rev < 6 // restore hi and lo ld $8, (8 * 33)($4) mthi $8 ld $8, (8 * 34)($4) mtlo $8 +#endif // r0 is zero ld $1, (8 * 1)($4) ld $2, (8 * 2)($4) diff --git a/src/native/external/llvm-libunwind/src/UnwindRegistersSave.S b/src/native/external/llvm-libunwind/src/UnwindRegistersSave.S index 79f5696a9888f..19a0e87d683ce 100644 --- a/src/native/external/llvm-libunwind/src/UnwindRegistersSave.S +++ b/src/native/external/llvm-libunwind/src/UnwindRegistersSave.S @@ -174,11 +174,13 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) sw $31, (4 * 31)($4) # Store return address to pc sw $31, (4 * 32)($4) +#if __mips_isa_rev < 6 # hi and lo mfhi $8 sw $8, (4 * 33)($4) mflo $8 sw $8, (4 * 34)($4) +#endif #ifdef __mips_hard_float #if __mips_fpr != 64 sdc1 $f0, (4 * 36 + 8 * 0)($4) @@ -255,11 +257,13 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) .endr # Store return address to pc sd $31, (8 * 32)($4) +#if __mips_isa_rev < 6 # hi and lo mfhi $8 sd $8, (8 * 33)($4) mflo $8 sd $8, (8 * 34)($4) +#endif #ifdef __mips_hard_float .irp i,FROM_0_TO_31 sdc1 $f\i, (280+8*\i)($4) @@ -301,9 +305,21 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) mflr 0 std 0, PPC64_OFFS_SRR0(3) // store lr as ssr0 PPC64_STR(1) + PPC64_STR(4) // Save r4 first since it will be used for fixing r2. +#if defined(_AIX) + // The TOC register (r2) was changed by the glue code if unw_getcontext + // is called from a different module. Save the original TOC register + // in the context if this is the case. + mflr 4 + lwz 4, 0(4) // Get the first instruction at the return address. + xoris 0, 4, 0xe841 // Is it reloading the TOC register "ld 2,40(1)"? + cmplwi 0, 0x28 + bne 0, LnoR2Fix // No need to fix up r2 if it is not. + ld 2, 40(1) // Use the saved TOC register in the stack. +LnoR2Fix: +#endif PPC64_STR(2) PPC64_STR(3) - PPC64_STR(4) PPC64_STR(5) PPC64_STR(6) PPC64_STR(7) @@ -336,7 +352,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) std 0, PPC64_OFFS_CR(3) mfxer 0 std 0, PPC64_OFFS_XER(3) +#if defined(_AIX) + // LR value saved from the register is not used, initialize it to 0. + li 0, 0 +#else mflr 0 +#endif std 0, PPC64_OFFS_LR(3) mfctr 0 std 0, PPC64_OFFS_CTR(3) @@ -543,9 +564,21 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) mflr 0 stw 0, 0(3) // store lr as ssr0 stw 1, 12(3) + stw 4, 24(3) // Save r4 first since it will be used for fixing r2. +#if defined(_AIX) + // The TOC register (r2) was changed by the glue code if unw_getcontext + // is called from a different module. Save the original TOC register + // in the context if this is the case. + mflr 4 + lwz 4, 0(4) // Get the instruction at the return address. + xoris 0, 4, 0x8041 // Is it reloading the TOC register "lwz 2,20(1)"? + cmplwi 0, 0x14 + bne 0, LnoR2Fix // No need to fix up r2 if it is not. + lwz 2, 20(1) // Use the saved TOC register in the stack. +LnoR2Fix: +#endif stw 2, 16(3) stw 3, 20(3) - stw 4, 24(3) stw 5, 28(3) stw 6, 32(3) stw 7, 36(3) @@ -582,6 +615,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) // save CR registers mfcr 0 stw 0, 136(3) +#if defined(_AIX) + // LR value from the register is not used, initialize it to 0. + li 0, 0 + stw 0, 144(3) +#endif // save CTR register mfctr 0 stw 0, 148(3) @@ -742,7 +780,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) @ @ On entry: @ thread_state pointer is in r0 -@ +@ @ Per EHABI #4.7 this only saves the core integer registers. @ EHABI #7.4.5 notes that in general all VRS registers should be restored @ however this is very hard to do for VFP registers because it is unknown diff --git a/src/native/external/llvm-libunwind/src/Unwind_AppleExtras.cpp b/src/native/external/llvm-libunwind/src/Unwind_AppleExtras.cpp deleted file mode 100644 index ffb49a89e54f3..0000000000000 --- a/src/native/external/llvm-libunwind/src/Unwind_AppleExtras.cpp +++ /dev/null @@ -1,113 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// -//===----------------------------------------------------------------------===// - -#include "config.h" - - -// static linker symbols to prevent wrong two level namespace for _Unwind symbols -#if defined(__arm__) - #define NOT_HERE_BEFORE_5_0(sym) \ - extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp30 = 0; \ - extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp31 = 0; \ - extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\ - __attribute__((visibility("default"))) const char sym##_tmp32 = 0; \ - extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp40 = 0; \ - extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp41 = 0; \ - extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp42 = 0; \ - extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp43 = 0; -#elif defined(__aarch64__) - #define NOT_HERE_BEFORE_10_6(sym) - #define NEVER_HERE(sym) -#else - #define NOT_HERE_BEFORE_10_6(sym) \ - extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ - extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp5 = 0; - #define NEVER_HERE(sym) \ - extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ - extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ - extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp6 = 0; -#endif - - -#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) - -// -// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in -// earlier versions -// -NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) -NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) -NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) -NOT_HERE_BEFORE_10_6(_Unwind_GetGR) -NOT_HERE_BEFORE_10_6(_Unwind_GetIP) -NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) -NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) -NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) -NOT_HERE_BEFORE_10_6(_Unwind_Resume) -NOT_HERE_BEFORE_10_6(_Unwind_SetGR) -NOT_HERE_BEFORE_10_6(_Unwind_SetIP) -NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) -NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) -NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) -NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) -NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) -NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) -NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) -NOT_HERE_BEFORE_10_6(__register_frame) -NOT_HERE_BEFORE_10_6(__deregister_frame) - -// -// symbols in libSystem.dylib for compatibility, but we don't want any new code -// using them -// -NEVER_HERE(__register_frame_info_bases) -NEVER_HERE(__register_frame_info) -NEVER_HERE(__register_frame_info_table_bases) -NEVER_HERE(__register_frame_info_table) -NEVER_HERE(__register_frame_table) -NEVER_HERE(__deregister_frame_info) -NEVER_HERE(__deregister_frame_info_bases) - -#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) - - - - -#if defined(_LIBUNWIND_BUILD_SJLJ_APIS) -// -// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in -// earlier versions -// -NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData) -NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart) -NOT_HERE_BEFORE_5_0(_Unwind_GetIP) -NOT_HERE_BEFORE_5_0(_Unwind_SetGR) -NOT_HERE_BEFORE_5_0(_Unwind_SetIP) -NOT_HERE_BEFORE_5_0(_Unwind_DeleteException) -NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register) -NOT_HERE_BEFORE_5_0(_Unwind_GetGR) -NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo) -NOT_HERE_BEFORE_5_0(_Unwind_GetCFA) -NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume) -NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException) -NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow) -NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister) - -#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS) diff --git a/src/native/external/llvm-libunwind/src/config.h b/src/native/external/llvm-libunwind/src/config.h index 4bbac951624f9..deb5a4d4d73d4 100644 --- a/src/native/external/llvm-libunwind/src/config.h +++ b/src/native/external/llvm-libunwind/src/config.h @@ -46,6 +46,12 @@ #elif defined(_AIX) // The traceback table at the end of each function is used for unwinding. #define _LIBUNWIND_SUPPORT_TBTAB_UNWIND 1 +#elif defined(__HAIKU__) + #if defined(_LIBUNWIND_USE_HAIKU_BSD_LIB) + #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1 + #endif + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 #else // Assume an ELF system with a dl_iterate_phdr function. #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1 @@ -83,7 +89,7 @@ __asm__(".globl " SYMBOL_NAME(aliasname)); \ __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \ _LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname)) -#elif defined(__ELF__) || defined(_AIX) +#elif defined(__ELF__) || defined(_AIX) || defined(__wasm__) #define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ __attribute__((weak, alias(#name))); @@ -108,10 +114,6 @@ #define _LIBUNWIND_BUILD_SJLJ_APIS #endif -#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) -#define _LIBUNWIND_SUPPORT_FRAME_APIS -#endif - #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \ (!defined(__APPLE__) && defined(__arm__)) || defined(__aarch64__) || \ defined(__mips__) || defined(__riscv) || defined(__hexagon__) || \ @@ -125,7 +127,7 @@ #if defined(_LIBUNWIND_REMEMBER_STACK_ALLOC) || defined(__APPLE__) || \ defined(__linux__) || defined(__ANDROID__) || defined(__MINGW32__) || \ defined(_LIBUNWIND_IS_BAREMETAL) -#define _LIBUNWIND_REMEMBER_ALLOC(_size) alloca(_size) +#define _LIBUNWIND_REMEMBER_ALLOC(_size) __builtin_alloca(_size) #define _LIBUNWIND_REMEMBER_FREE(_ptr) \ do { \ } while (0) @@ -162,10 +164,14 @@ #define _LIBUNWIND_LOG0(msg) #define _LIBUNWIND_LOG(msg, ...) #else -#define _LIBUNWIND_LOG0(msg) \ - fprintf(stderr, "libunwind: " msg "\n") -#define _LIBUNWIND_LOG(msg, ...) \ - fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__) +#define _LIBUNWIND_LOG0(msg) do { \ + fprintf(stderr, "libunwind: " msg "\n"); \ + fflush(stderr); \ + } while (0) +#define _LIBUNWIND_LOG(msg, ...) do { \ + fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__); \ + fflush(stderr); \ + } while (0) #endif #if defined(NDEBUG) diff --git a/src/native/external/llvm-libunwind/src/libunwind.cpp b/src/native/external/llvm-libunwind/src/libunwind.cpp index f350655b869fe..96e4343a2cc67 100644 --- a/src/native/external/llvm-libunwind/src/libunwind.cpp +++ b/src/native/external/llvm-libunwind/src/libunwind.cpp @@ -26,7 +26,7 @@ #include #endif -#if !defined(__USING_SJLJ_EXCEPTIONS__) +#if !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__USING_WASM_EXCEPTIONS__) #include "AddressSpace.hpp" #include "UnwindCursor.hpp" @@ -339,7 +339,7 @@ void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) { CFI_Parser::CIE_Info cieInfo; CFI_Parser::FDE_Info fdeInfo; auto p = (LocalAddressSpace::pint_t)eh_frame_start; - while (true) { + while (LocalAddressSpace::sThisAddressSpace.get32(p)) { if (CFI_Parser::decodeFDE( LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo, true) == NULL) { @@ -362,9 +362,90 @@ void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) { } #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) -#endif // !defined(__USING_SJLJ_EXCEPTIONS__) +#endif // !defined(__USING_SJLJ_EXCEPTIONS__) && + // !defined(__USING_WASM_EXCEPTIONS__) +#ifdef __APPLE__ +namespace libunwind { + +static constexpr size_t MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS = 8; + +static RWMutex findDynamicUnwindSectionsLock; +static size_t numDynamicUnwindSectionsFinders = 0; +static unw_find_dynamic_unwind_sections + dynamicUnwindSectionsFinders[MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS] = {0}; + +bool findDynamicUnwindSections(void *addr, unw_dynamic_unwind_sections *info) { + bool found = false; + findDynamicUnwindSectionsLock.lock_shared(); + for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { + if (dynamicUnwindSectionsFinders[i]((unw_word_t)addr, info)) { + found = true; + break; + } + } + findDynamicUnwindSectionsLock.unlock_shared(); + return found; +} + +} // namespace libunwind + +int __unw_add_find_dynamic_unwind_sections( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) { + findDynamicUnwindSectionsLock.lock(); + + // Check that we have enough space... + if (numDynamicUnwindSectionsFinders == MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS) { + findDynamicUnwindSectionsLock.unlock(); + return UNW_ENOMEM; + } + + // Check for value already present... + for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { + if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) { + findDynamicUnwindSectionsLock.unlock(); + return UNW_EINVAL; + } + } + + // Success -- add callback entry. + dynamicUnwindSectionsFinders[numDynamicUnwindSectionsFinders++] = + find_dynamic_unwind_sections; + findDynamicUnwindSectionsLock.unlock(); + + return UNW_ESUCCESS; +} + +int __unw_remove_find_dynamic_unwind_sections( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) { + findDynamicUnwindSectionsLock.lock(); + + // Find index to remove. + size_t finderIdx = numDynamicUnwindSectionsFinders; + for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { + if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) { + finderIdx = i; + break; + } + } + + // If no such registration is present then error out. + if (finderIdx == numDynamicUnwindSectionsFinders) { + findDynamicUnwindSectionsLock.unlock(); + return UNW_EINVAL; + } + + // Remove entry. + for (size_t i = finderIdx; i != numDynamicUnwindSectionsFinders - 1; ++i) + dynamicUnwindSectionsFinders[i] = dynamicUnwindSectionsFinders[i + 1]; + dynamicUnwindSectionsFinders[--numDynamicUnwindSectionsFinders] = nullptr; + + findDynamicUnwindSectionsLock.unlock(); + return UNW_ESUCCESS; +} + +#endif // __APPLE__ // Add logging hooks in Debug builds only #ifndef NDEBUG diff --git a/src/native/external/llvm-libunwind/src/libunwind_ext.h b/src/native/external/llvm-libunwind/src/libunwind_ext.h index 5385821ce2c12..db55939971264 100644 --- a/src/native/external/llvm-libunwind/src/libunwind_ext.h +++ b/src/native/external/llvm-libunwind/src/libunwind_ext.h @@ -59,6 +59,71 @@ extern void __unw_remove_dynamic_fde(unw_word_t fde); extern void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start); extern void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start); +#ifdef __APPLE__ + +// Holds a description of the object-format-header (if any) and unwind info +// sections for a given address: +// +// * dso_base should point to a header for the JIT'd object containing the +// given address. The header's type should match the format type that +// libunwind was compiled for (so a mach_header or mach_header_64 on Darwin). +// A value of zero indicates that no such header exists. +// +// * dwarf_section and dwarf_section_length hold the address range of a DWARF +// eh-frame section associated with the given address, if any. If the +// dwarf_section_length field is zero it indicates that no such section +// exists (and in this case dwarf_section should also be set to zero). +// +// * compact_unwind_section and compact_unwind_section_length hold the address +// range of a compact-unwind info section associated with the given address, +// if any. If the compact_unwind_section_length field is zero it indicates +// that no such section exists (and in this case compact_unwind_section +// should also be set to zero). +// +// See the unw_find_dynamic_unwind_sections type below for more details. +struct unw_dynamic_unwind_sections { + unw_word_t dso_base; + unw_word_t dwarf_section; + size_t dwarf_section_length; + unw_word_t compact_unwind_section; + size_t compact_unwind_section_length; +}; + +// Typedef for unwind-info lookup callbacks. Functions of this type can be +// registered and deregistered using __unw_add_find_dynamic_unwind_sections +// and __unw_remove_find_dynamic_unwind_sections respectively. +// +// An unwind-info lookup callback should return 1 to indicate that it found +// unwind-info for the given address, or 0 to indicate that it did not find +// unwind-info for the given address. If found, the callback should populate +// some or all of the fields of the info argument (which is guaranteed to be +// non-null with all fields zero-initialized): +typedef int (*unw_find_dynamic_unwind_sections)( + unw_word_t addr, struct unw_dynamic_unwind_sections *info); + +// Register a dynamic unwind-info lookup callback. If libunwind does not find +// unwind info for a given frame in the executable program or normal dynamic +// shared objects then it will call all registered dynamic lookup functions +// in registration order until either one of them returns true, or the end +// of the list is reached. This lookup will happen before libunwind searches +// any eh-frames registered via __register_frame or +// __unw_add_dynamic_eh_frame_section. +// +// Returns UNW_ESUCCESS for successful registrations. If the given callback +// has already been registered then UNW_EINVAL will be returned. If all +// available callback entries are in use then UNW_ENOMEM will be returned. +extern int __unw_add_find_dynamic_unwind_sections( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections); + +// Deregister a dynacim unwind-info lookup callback. +// +// Returns UNW_ESUCCESS for successful deregistrations. If the given callback +// has already been registered then UNW_EINVAL will be returned. +extern int __unw_remove_find_dynamic_unwind_sections( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections); + +#endif + #if defined(_LIBUNWIND_ARM_EHABI) extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*); extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, diff --git a/src/native/external/llvm-libunwind/src/unwind_ext.h b/src/native/external/llvm-libunwind/src/unwind_ext.h deleted file mode 100644 index c40ce6a1610f4..0000000000000 --- a/src/native/external/llvm-libunwind/src/unwind_ext.h +++ /dev/null @@ -1,37 +0,0 @@ -//===-------------------------- unwind_ext.h ------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -// -// Extensions to unwind API. -// -//===----------------------------------------------------------------------===// - -#ifndef __UNWIND_EXT__ -#define __UNWIND_EXT__ - -#include "unwind.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// These platform specific functions to get and set the top context are -// implemented elsewhere. - -extern struct _Unwind_FunctionContext * -__Unwind_SjLj_GetTopOfFunctionStack(); - -extern void -__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc); - -#ifdef __cplusplus -} -#endif - -#endif // __UNWIND_EXT__ - - diff --git a/src/native/external/llvm-libunwind/test/CMakeLists.txt b/src/native/external/llvm-libunwind/test/CMakeLists.txt index 0a09553e692da..21dfbb0a84f0a 100644 --- a/src/native/external/llvm-libunwind/test/CMakeLists.txt +++ b/src/native/external/llvm-libunwind/test/CMakeLists.txt @@ -1,4 +1,5 @@ include(AddLLVM) # for add_lit_testsuite +include(HandleLitArguments) macro(pythonize_bool var) if (${var}) set(${var} True) @@ -10,33 +11,28 @@ endmacro() pythonize_bool(LIBUNWIND_ENABLE_CET) pythonize_bool(LIBUNWIND_ENABLE_THREADS) pythonize_bool(LIBUNWIND_USES_ARM_EHABI) -set(LIBUNWIND_EXECUTOR "${Python3_EXECUTABLE} ${LIBUNWIND_LIBCXX_PATH}/utils/run.py" CACHE STRING - "Executor to use when running tests.") set(AUTO_GEN_COMMENT "## Autogenerated by libunwind configuration.\n# Do not edit!") set(SERIALIZED_LIT_PARAMS "# Lit parameters serialized here for llvm-lit to pick them up\n") -macro(serialize_lit_param param value) - string(APPEND SERIALIZED_LIT_PARAMS "config.${param} = ${value}\n") -endmacro() +if (LIBUNWIND_EXECUTOR) + message(DEPRECATION "LIBUNWIND_EXECUTOR is deprecated, please add executor=... to LIBUNWIND_TEST_PARAMS") + serialize_lit_string_param(SERIALIZED_LIT_PARAMS executor "${LIBUNWIND_EXECUTOR}") +endif() -serialize_lit_param(enable_experimental False) +serialize_lit_param(SERIALIZED_LIT_PARAMS enable_experimental False) if (LLVM_USE_SANITIZER) - serialize_lit_param(use_sanitizer "\"${LLVM_USE_SANITIZER}\"") + serialize_lit_string_param(SERIALIZED_LIT_PARAMS use_sanitizer "${LLVM_USE_SANITIZER}") endif() if (CMAKE_CXX_COMPILER_TARGET) - serialize_lit_param(target_triple "\"${CMAKE_CXX_COMPILER_TARGET}\"") + serialize_lit_string_param(SERIALIZED_LIT_PARAMS target_triple "${CMAKE_CXX_COMPILER_TARGET}") else() - serialize_lit_param(target_triple "\"${LLVM_DEFAULT_TARGET_TRIPLE}\"") + serialize_lit_string_param(SERIALIZED_LIT_PARAMS target_triple "${LLVM_DEFAULT_TARGET_TRIPLE}") endif() -foreach(param IN LISTS LIBUNWIND_TEST_PARAMS) - string(REGEX REPLACE "(.+)=(.+)" "\\1" name "${param}") - string(REGEX REPLACE "(.+)=(.+)" "\\2" value "${param}") - serialize_lit_param("${name}" "\"${value}\"") -endforeach() +serialize_lit_params_list(SERIALIZED_LIT_PARAMS LIBUNWIND_TEST_PARAMS) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/cmake-bridge.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake-bridge.cfg" diff --git a/src/native/external/llvm-libunwind/test/aix_signal_unwind.pass.sh.S b/src/native/external/llvm-libunwind/test/aix_signal_unwind.pass.sh.S new file mode 100644 index 0000000000000..9ca18e9481f4f --- /dev/null +++ b/src/native/external/llvm-libunwind/test/aix_signal_unwind.pass.sh.S @@ -0,0 +1,245 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Test that _Unwind_Backtrace() walks up from a signal handler and produces +// a correct traceback when the function raising the signal does not save +// the link register or does not store the stack back chain. + +// REQUIRES: target=powerpc{{(64)?}}-ibm-aix + +// Test when the function raising the signal does not save the link register +// RUN: %{cxx} -x c++ %s -o %t.exe -DCXX_CODE %{flags} %{compile_flags} +// RUN: %{exec} %t.exe + +// Test when the function raising the signal does not store stack back chain. +// RUN: %{cxx} -x c++ -c %s -o %t1.o -DCXX_CODE -DNOBACKCHAIN %{flags} \ +// RUN: %{compile_flags} +// RUN: %{cxx} -c %s -o %t2.o %{flags} %{compile_flags} +// RUN: %{cxx} -o %t1.exe %t1.o %t2.o %{flags} %{link_flags} +// RUN: %{exec} %t1.exe + +#ifdef CXX_CODE + +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include + +#define NAME_ARRAY_SIZE 10 +#define NAMES_EXPECTED 6 + +const char* namesExpected[] = {"handler", "abc", "bar", "foo", "main", + "__start"}; +char *namesObtained[NAME_ARRAY_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +int funcIndex = 0; + +// Get the function name from traceback table. +char *getFuncName(uintptr_t pc, uint16_t *nameLen) { + uint32_t *p = reinterpret_cast(pc); + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable *TBTable = reinterpret_cast(p + 1); + + if (!TBTable->tb.name_present) + return NULL; + + // Get to the optional portion of the traceback table. + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // Skip field tb_offset if it exists. + if (TBTable->tb.has_tboff) + ++p; + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + ++p; + + // Skip fields ctl_info and ctl_info_disp if they exist. + if (TBTable->tb.has_ctl) + p += 1 + *p; + + *nameLen = *reinterpret_cast(p); + return reinterpret_cast(p) + sizeof(uint16_t); +} + +_Unwind_Reason_Code callBack(struct _Unwind_Context *uc, void *arg) { + (void)arg; + uint16_t nameLen; + uintptr_t ip = _Unwind_GetIP(uc); + if (funcIndex < NAME_ARRAY_SIZE) + namesObtained[funcIndex++] = strndup(getFuncName(ip, &nameLen), nameLen); + return _URC_NO_REASON; +} + +extern "C" void handler(int signum) { + (void)signum; + // Walk stack frames for traceback. + _Unwind_Backtrace(callBack, NULL); + + // Verify the traceback. + assert(funcIndex <= NAMES_EXPECTED && "Obtained names more than expected"); + for (int i = 0; i < funcIndex; ++i) { + assert(!strcmp(namesExpected[i], namesObtained[i]) && + "Function names do not match"); + free(namesObtained[i]); + } + exit(0); +} + +#ifdef NOBACKCHAIN +// abc() is in assembly. It raises signal SIGSEGV and does not store +// the stack back chain. +extern "C" void abc(); + +#else +volatile int *null = 0; + +// abc() raises signal SIGSEGV and does not save the link register. +extern "C" __attribute__((noinline)) void abc() { + // Produce a SIGSEGV. + *null = 0; +} +#endif + +extern "C" __attribute__((noinline)) void bar() { + abc(); +} + +extern "C" __attribute__((noinline)) void foo() { + bar(); +} + +int main() { + // Set signal handler for SIGSEGV. + signal(SIGSEGV, handler); + foo(); +} + +#else // Assembly code for abc(). +// This assembly code is similar to the following C code but it saves the +// link register. +// +// int *badp = 0; +// void abc() { +// *badp = 0; +// } + +#ifdef __64BIT__ + .csect [PR],5 + .file "abc.c" + .globl abc[DS] # -- Begin function abc + .globl .abc + .align 4 + .csect abc[DS],3 + .vbyte 8, .abc # @abc + .vbyte 8, TOC[TC0] + .vbyte 8, 0 + .csect [PR],5 +.abc: +# %bb.0: # %entry + mflr 0 + std 0, 16(1) + ld 3, L..C0(2) # @badp + bl $+4 + ld 4, 0(3) + li 3, 0 + stw 3, 0(4) + ld 0, 16(1) + mtlr 0 + blr +L..abc0: + .vbyte 4, 0x00000000 # Traceback table begin + .byte 0x00 # Version = 0 + .byte 0x09 # Language = CPlusPlus + .byte 0x20 # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue + # +HasTraceBackTableOffset, -IsInternalProcedure + # -HasControlledStorage, -IsTOCless + # -IsFloatingPointPresent + # -IsFloatingPointOperationLogOrAbortEnabled + .byte 0x61 # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed + # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved + .byte 0x00 # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0 + .byte 0x01 # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1 + .byte 0x00 # NumberOfFixedParms = 0 + .byte 0x01 # NumberOfFPParms = 0, +HasParmsOnStack + .vbyte 4, L..abc0-.abc # Function size + .vbyte 2, 0x0003 # Function name len = 3 + .byte "abc" # Function Name + .byte 0x1f # AllocaUsed + # -- End function + .csect badp[RW],3 + .globl badp[RW] # @badp + .align 3 + .vbyte 8, 0 + .toc +L..C0: + .tc badp[TC],badp[RW] +#else + .csect [PR],5 + .file "abc.c" + .globl abc[DS] # -- Begin function abc + .globl .abc + .align 4 + .csect abc[DS],2 + .vbyte 4, .abc # @abc + .vbyte 4, TOC[TC0] + .vbyte 4, 0 + .csect [PR],5 +.abc: +# %bb.0: # %entry + mflr 0 + stw 0, 8(1) + lwz 3, L..C0(2) # @badp + bl $+4 + lwz 4, 0(3) + li 3, 0 + stw 3, 0(4) + lwz 0, 8(1) + mtlr 0 + blr +L..abc0: + .vbyte 4, 0x00000000 # Traceback table begin + .byte 0x00 # Version = 0 + .byte 0x09 # Language = CPlusPlus + .byte 0x20 # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue + # +HasTraceBackTableOffset, -IsInternalProcedure + # -HasControlledStorage, -IsTOCless + # -IsFloatingPointPresent + # -IsFloatingPointOperationLogOrAbortEnabled + .byte 0x61 # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed + # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved + .byte 0x00 # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0 + .byte 0x01 # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1 + .byte 0x00 # NumberOfFixedParms = 0 + .byte 0x01 # NumberOfFPParms = 0, +HasParmsOnStack + .vbyte 4, L..abc0-.abc # Function size + .vbyte 2, 0x0003 # Function name len = 3 + .byte "abc" # Function Name + .byte 0x1f # AllocaUsed + # -- End function + .csect badp[RW],2 + .globl badp[RW] # @badp + .align 2 + .vbyte 4, 0 + .toc +L..C0: + .tc badp[TC],badp[RW] +#endif // __64BIT__ +#endif // CXX_CODE diff --git a/src/native/external/llvm-libunwind/test/bad_unwind_info.pass.cpp b/src/native/external/llvm-libunwind/test/bad_unwind_info.pass.cpp index 06017d17ab574..b3284e8daed71 100644 --- a/src/native/external/llvm-libunwind/test/bad_unwind_info.pass.cpp +++ b/src/native/external/llvm-libunwind/test/bad_unwind_info.pass.cpp @@ -10,7 +10,7 @@ // Ensure that libunwind doesn't crash on invalid info; the Linux aarch64 // sigreturn frame check would previously attempt to access invalid memory in // this scenario. -// REQUIRES: linux && (target={{aarch64-.+}} || target={{s390x-.+}} || target={{x86_64-.+}}) +// REQUIRES: target={{(aarch64|s390x|x86_64)-.+linux.*}} // GCC doesn't support __attribute__((naked)) on AArch64. // UNSUPPORTED: gcc diff --git a/src/native/external/llvm-libunwind/test/configs/apple-libunwind-backdeployment.cfg.in b/src/native/external/llvm-libunwind/test/configs/apple-libunwind-backdeployment.cfg.in index d4777e2f6917a..4484573801bd2 100644 --- a/src/native/external/llvm-libunwind/test/configs/apple-libunwind-backdeployment.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/apple-libunwind-backdeployment.cfg.in @@ -7,7 +7,7 @@ import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.dsl +import libcxx.test.params, libcxx.test.config, libcxx.test.dsl lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') @@ -54,9 +54,11 @@ config.substitutions.append(('%{exec}', '%{executor} --execdir %T --env DYLD_LIBRARY_PATH="%{cxx-runtime-root}:%{abi-runtime-root}:%{unwind-runtime-root}" -- ' )) +config.stdlib = 'apple-libc++' + import os, site -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.newconfig -libcxx.test.newconfig.configure( +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS + BACKDEPLOYMENT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, config, diff --git a/src/native/external/llvm-libunwind/test/configs/armv7m-picolibc-libunwind.cfg.in b/src/native/external/llvm-libunwind/test/configs/armv7m-picolibc-libunwind.cfg.in new file mode 100644 index 0000000000000..e8f68a51fc53f --- /dev/null +++ b/src/native/external/llvm-libunwind/test/configs/armv7m-picolibc-libunwind.cfg.in @@ -0,0 +1,39 @@ +lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') + +libc_linker_script = '@CMAKE_INSTALL_PREFIX@/lib/picolibcpp.ld' + +config.substitutions.append(('%{flags}', '--sysroot=@CMAKE_INSTALL_PREFIX@')) + +config.substitutions.append(('%{compile_flags}', + '-nostdinc++ -I %{include}' +)) +config.substitutions.append(('%{link_flags}', + '-nostdlib -nostdlib++ -L %{lib} -lunwind' + ' -lc -lm -lclang_rt.builtins -lsemihost -lcrt0-semihost' + + ' -T {}'.format(libc_linker_script) + + ' -Wl,--defsym=__flash=0x0' + ' -Wl,--defsym=__flash_size=0x400000' + ' -Wl,--defsym=__ram=0x21000000' + ' -Wl,--defsym=__ram_size=0x1000000' + ' -Wl,--defsym=__stack_size=0x1000' +)) + +config.executor = ( + '@LIBUNWIND_LIBCXX_PATH@/utils/qemu_baremetal.py' + ' --qemu @QEMU_SYSTEM_ARM@' + ' --machine mps2-an385' + ' --cpu cortex-m3') +config.substitutions.append(('%{exec}', + '%{executor}' + ' --execdir %T' +)) + +import os, site +site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( + libcxx.test.params.DEFAULT_PARAMETERS, + libcxx.test.features.DEFAULT_FEATURES, + config, + lit_config +) diff --git a/src/native/external/llvm-libunwind/test/configs/cmake-bridge.cfg.in b/src/native/external/llvm-libunwind/test/configs/cmake-bridge.cfg.in index 109602db8a0fb..c5f34c87abb92 100644 --- a/src/native/external/llvm-libunwind/test/configs/cmake-bridge.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/cmake-bridge.cfg.in @@ -20,7 +20,7 @@ config.name = os.path.basename('@LIBUNWIND_TEST_CONFIG@') config.test_source_root = os.path.join('@LIBUNWIND_SOURCE_DIR@', 'test') config.test_format = libcxx.test.format.CxxStandardLibraryTest() config.recursiveExpansionLimit = 10 -config.test_exec_root = '@CMAKE_BINARY_DIR@' +config.test_exec_root = os.path.join('@CMAKE_BINARY_DIR@', 'test') # Add a few features that are common to all the configurations if @LIBUNWIND_USES_ARM_EHABI@: @@ -31,6 +31,5 @@ if not @LIBUNWIND_ENABLE_THREADS@: # Add substitutions for bootstrapping the test suite configuration import shlex config.substitutions.append(('%{cxx}', shlex.quote('@CMAKE_CXX_COMPILER@'))) -config.substitutions.append(('%{executor}', '@LIBUNWIND_EXECUTOR@')) config.substitutions.append(('%{include}', '@LIBUNWIND_SOURCE_DIR@/include')) config.substitutions.append(('%{lib}', '@LIBUNWIND_LIBRARY_DIR@')) diff --git a/src/native/external/llvm-libunwind/test/configs/ibm-libunwind-shared.cfg.in b/src/native/external/llvm-libunwind/test/configs/ibm-libunwind-shared.cfg.in index 599de72b1399d..2221e0cf499ff 100644 --- a/src/native/external/llvm-libunwind/test/configs/ibm-libunwind-shared.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/ibm-libunwind-shared.cfg.in @@ -3,6 +3,12 @@ lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') +import lit.util +if lit.util.isAIXTriple(config.target_triple): + # Add the AIX version to the triple here because there currently isn't a good + # way to retrieve the AIX version in the driver. + config.target_triple = lit.util.addAIXVersion(config.target_triple) + config.substitutions.append(('%{flags}', '')) config.substitutions.append(('%{compile_flags}', '-nostdinc++ -I %{include}' @@ -16,8 +22,8 @@ config.substitutions.append(('%{exec}', import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.newconfig -libcxx.test.newconfig.configure( +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, config, diff --git a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-merged.cfg.in b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-merged.cfg.in index d01ee7470de7b..38b79840c9fe2 100644 --- a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-merged.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-merged.cfg.in @@ -11,8 +11,12 @@ link_flags = [] if @LIBUNWIND_ENABLE_CET@: compile_flags.append('-fcf-protection=full') -if '@CMAKE_SYSTEM_NAME@' == 'Linux': - link_flags.append('-Wl,--export-dynamic') +# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker. +if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'): + link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@') + +if '@CMAKE_DL_LIBS@': + link_flags.append('-l@CMAKE_DL_LIBS@') # Stack unwinding tests need unwinding tables and these are not generated by default on all targets. compile_flags.append('-funwind-tables') @@ -25,7 +29,7 @@ config.substitutions.append(('%{compile_flags}', '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags)) )) config.substitutions.append(('%{link_flags}', - '-L %{{lib}} -Wl,-rpath,%{{lib}} -lc++ -ldl {}'.format(' '.join(link_flags)) + '-L %{{lib}} -Wl,-rpath,%{{lib}} -lc++ {}'.format(' '.join(link_flags)) )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T -- ' @@ -33,8 +37,8 @@ config.substitutions.append(('%{exec}', import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.newconfig -libcxx.test.newconfig.configure( +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, config, diff --git a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-mingw.cfg.in b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-mingw.cfg.in new file mode 100644 index 0000000000000..33d061a3efc6f --- /dev/null +++ b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-mingw.cfg.in @@ -0,0 +1,25 @@ +# This testing configuration handles running the test suite against LLVM's libunwind +# using either a DLL or a static library, with MinGW/Clang on Windows. + +lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg') + +config.substitutions.append(('%{flags}', '')) +config.substitutions.append(('%{compile_flags}', + '-nostdinc++ -I %{include} -funwind-tables' +)) +config.substitutions.append(('%{link_flags}', + '-L %{lib} -lunwind' +)) +config.substitutions.append(('%{exec}', + '%{executor} --execdir %T --prepend_env PATH=%{lib} -- ' +)) + +import os, site +site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( + libcxx.test.params.DEFAULT_PARAMETERS, + libcxx.test.features.DEFAULT_FEATURES, + config, + lit_config +) diff --git a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-shared.cfg.in b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-shared.cfg.in index b82eec71b02ff..13896aeb13bc4 100644 --- a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-shared.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-shared.cfg.in @@ -10,8 +10,12 @@ link_flags = [] if @LIBUNWIND_ENABLE_CET@: compile_flags.append('-fcf-protection=full') -if '@CMAKE_SYSTEM_NAME@' == 'Linux': - link_flags.append('-Wl,--export-dynamic') +# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker. +if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'): + link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@') + +if '@CMAKE_DL_LIBS@': + link_flags.append('-l@CMAKE_DL_LIBS@') # Stack unwinding tests need unwinding tables and these are not generated by default on all targets. compile_flags.append('-funwind-tables') @@ -24,7 +28,7 @@ config.substitutions.append(('%{compile_flags}', '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags)) )) config.substitutions.append(('%{link_flags}', - '-L %{{lib}} -Wl,-rpath,%{{lib}} -lunwind -ldl {}'.format(' '.join(link_flags)) + '-L %{{lib}} -Wl,-rpath,%{{lib}} -lunwind {}'.format(' '.join(link_flags)) )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T -- ' @@ -32,8 +36,8 @@ config.substitutions.append(('%{exec}', import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.newconfig -libcxx.test.newconfig.configure( +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, config, diff --git a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-static.cfg.in b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-static.cfg.in index a947a5d08f58b..50b64dc665a5a 100644 --- a/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-static.cfg.in +++ b/src/native/external/llvm-libunwind/test/configs/llvm-libunwind-static.cfg.in @@ -13,8 +13,12 @@ if @LIBUNWIND_ENABLE_THREADS@: if @LIBUNWIND_ENABLE_CET@: compile_flags.append('-fcf-protection=full') -if '@CMAKE_SYSTEM_NAME@' == 'Linux': - link_flags.append('-Wl,--export-dynamic') +# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker. +if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'): + link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@') + +if '@CMAKE_DL_LIBS@': + link_flags.append('-l@CMAKE_DL_LIBS@') # Stack unwinding tests need unwinding tables and these are not generated by default on all targets. compile_flags.append('-funwind-tables') @@ -27,7 +31,7 @@ config.substitutions.append(('%{compile_flags}', '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags)) )) config.substitutions.append(('%{link_flags}', - '%{{lib}}/libunwind.a -ldl {}'.format(' '.join(link_flags)) + '%{{lib}}/libunwind.a {}'.format(' '.join(link_flags)) )) config.substitutions.append(('%{exec}', '%{executor} --execdir %T -- ' @@ -35,8 +39,8 @@ config.substitutions.append(('%{exec}', import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) -import libcxx.test.params, libcxx.test.newconfig, libcxx.test.newconfig -libcxx.test.newconfig.configure( +import libcxx.test.params, libcxx.test.config +libcxx.test.config.configure( libcxx.test.params.DEFAULT_PARAMETERS, libcxx.test.features.DEFAULT_FEATURES, config, diff --git a/src/native/external/llvm-libunwind/test/forceunwind.pass.cpp b/src/native/external/llvm-libunwind/test/forceunwind.pass.cpp index 8c26551b6d0b6..db499d8bc3089 100644 --- a/src/native/external/llvm-libunwind/test/forceunwind.pass.cpp +++ b/src/native/external/llvm-libunwind/test/forceunwind.pass.cpp @@ -61,7 +61,7 @@ __attribute__((noinline)) void foo() { #if defined(_LIBUNWIND_ARM_EHABI) // Create a mock exception object. memset(e, '\0', sizeof(*e)); - strcpy(reinterpret_cast(&e->exception_class), "CLNGUNW"); + memcpy(&e->exception_class, "CLNGUNW", sizeof(e->exception_class)); #endif _Unwind_ForcedUnwind(e, stop, (void *)&foo); } diff --git a/src/native/external/llvm-libunwind/test/libunwind_01.pass.cpp b/src/native/external/llvm-libunwind/test/libunwind_01.pass.cpp index 4661ac9df8219..96f12d1fc3937 100644 --- a/src/native/external/llvm-libunwind/test/libunwind_01.pass.cpp +++ b/src/native/external/llvm-libunwind/test/libunwind_01.pass.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // TODO: Investigate this failure on x86_64 macOS back deployment -// XFAIL: use_system_cxx_lib && target=x86_64-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} +// XFAIL: stdlib=apple-libc++ && target=x86_64-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan diff --git a/src/native/external/llvm-libunwind/test/libunwind_02.pass.cpp b/src/native/external/llvm-libunwind/test/libunwind_02.pass.cpp index fc034378781a2..9fd8e5d7159c9 100644 --- a/src/native/external/llvm-libunwind/test/libunwind_02.pass.cpp +++ b/src/native/external/llvm-libunwind/test/libunwind_02.pass.cpp @@ -10,6 +10,9 @@ // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan +// This test fails on older llvm, when built with picolibc. +// XFAIL: clang-16 && LIBCXX-PICOLIBC-FIXME + #undef NDEBUG #include #include @@ -18,7 +21,8 @@ #define EXPECTED_NUM_FRAMES 50 #define NUM_FRAMES_UPPER_BOUND 100 -_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) { +__attribute__((noinline)) _Unwind_Reason_Code callback(_Unwind_Context *context, + void *cnt) { (void)context; int *i = (int *)cnt; ++*i; @@ -28,7 +32,7 @@ _Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) { return _URC_NO_REASON; } -void test_backtrace() { +__attribute__((noinline)) void test_backtrace() { int n = 0; _Unwind_Backtrace(&callback, &n); if (n < EXPECTED_NUM_FRAMES) { @@ -36,17 +40,34 @@ void test_backtrace() { } } -int test(int i) { +// These functions are effectively the same, but we have to be careful to avoid +// unwanted optimizations that would mess with the number of frames we expect. +// Surprisingly, slapping `noinline` is not sufficient -- we also have to avoid +// writing the function in a way that the compiler can easily spot tail +// recursion. +__attribute__((noinline)) int test1(int i); +__attribute__((noinline)) int test2(int i); + +__attribute__((noinline)) int test1(int i) { + if (i == 0) { + test_backtrace(); + return 0; + } else { + return i + test2(i - 1); + } +} + +__attribute__((noinline)) int test2(int i) { if (i == 0) { test_backtrace(); return 0; } else { - return i + test(i - 1); + return i + test1(i - 1); } } int main(int, char**) { - int total = test(50); + int total = test1(50); assert(total == 1275); return 0; } diff --git a/src/native/external/llvm-libunwind/test/lit.cfg.py b/src/native/external/llvm-libunwind/test/lit.cfg.py index 647464abe22d9..ea4a9a75b4fdb 100644 --- a/src/native/external/llvm-libunwind/test/lit.cfg.py +++ b/src/native/external/llvm-libunwind/test/lit.cfg.py @@ -7,4 +7,5 @@ lit_config.fatal( "You seem to be running Lit directly -- you should be running Lit through " "/bin/llvm-lit, which will ensure that the right Lit configuration " - "file is used.") + "file is used." +) diff --git a/src/native/external/llvm-libunwind/test/signal_frame.pass.cpp b/src/native/external/llvm-libunwind/test/signal_frame.pass.cpp index 482481d9d96ba..004029cfe1e90 100644 --- a/src/native/external/llvm-libunwind/test/signal_frame.pass.cpp +++ b/src/native/external/llvm-libunwind/test/signal_frame.pass.cpp @@ -19,7 +19,11 @@ // The AIX assembler does not support CFI directives, which // are necessary to run this test. -// UNSUPPORTED: target=powerpc{{(64)?}}-ibm-aix +// UNSUPPORTED: target={{.*}}-aix{{.*}} + +// Windows doesn't generally use CFI directives. However, i686 +// mingw targets do use DWARF (where CFI directives are supported). +// UNSUPPORTED: target={{x86_64|arm.*|aarch64}}-{{.*}}-windows-{{.*}} #undef NDEBUG #include diff --git a/src/native/external/llvm-libunwind/test/signal_unwind.pass.cpp b/src/native/external/llvm-libunwind/test/signal_unwind.pass.cpp index e6a53ab9ff952..954a5d4ba3db1 100644 --- a/src/native/external/llvm-libunwind/test/signal_unwind.pass.cpp +++ b/src/native/external/llvm-libunwind/test/signal_unwind.pass.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // Ensure that the unwinder can cope with the signal handler. -// REQUIRES: linux && (target={{aarch64-.+}} || target={{s390x-.+}} || target={{x86_64-.+}}) +// REQUIRES: target={{(aarch64|riscv64|s390x|x86_64)-.+linux.*}} // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan diff --git a/src/native/external/llvm-libunwind/test/unw_resume.pass.cpp b/src/native/external/llvm-libunwind/test/unw_resume.pass.cpp index 76273e4a8ef0a..2b7470b5cad0e 100644 --- a/src/native/external/llvm-libunwind/test/unw_resume.pass.cpp +++ b/src/native/external/llvm-libunwind/test/unw_resume.pass.cpp @@ -10,15 +10,12 @@ // Ensure that unw_resume() resumes execution at the stack frame identified by // cursor. -// TODO: Investigate this failure on AIX system. -// XFAIL: target={{.*}}-aix{{.*}} - // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan #include -void test_unw_resume() { +__attribute__((noinline)) void test_unw_resume() { unw_context_t context; unw_cursor_t cursor; diff --git a/src/native/external/llvm-libunwind/test/unwind_leaffunction.pass.cpp b/src/native/external/llvm-libunwind/test/unwind_leaffunction.pass.cpp index bdeb44ab62ae8..112a5968247a4 100644 --- a/src/native/external/llvm-libunwind/test/unwind_leaffunction.pass.cpp +++ b/src/native/external/llvm-libunwind/test/unwind_leaffunction.pass.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // Ensure that leaf function can be unwund. -// REQUIRES: linux && (target={{aarch64-.+}} || target={{s390x-.+}} || target={{x86_64-.+}}) +// REQUIRES: target={{(aarch64|riscv64|s390x|x86_64)-.+linux.*}} // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan @@ -28,7 +28,7 @@ _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { (void)arg; Dl_info info = { 0, 0, 0, 0 }; - // Unwind until the main is reached, above frames deeped on the platform and + // Unwind until the main is reached, above frames depend on the platform and // architecture. if (dladdr(reinterpret_cast(_Unwind_GetIP(ctx)), &info) && info.dli_sname && !strcmp("main", info.dli_sname)) { @@ -43,18 +43,22 @@ void signal_handler(int signum) { _Exit(-1); } -__attribute__((noinline)) void crashing_leaf_func(void) { +__attribute__((noinline)) void crashing_leaf_func(int do_trap) { // libunwind searches for the address before the return address which points - // to the trap instruction. NOP guarantees the trap instruction is not the - // first instruction of the function. - // We should keep this here for other unwinders that also decrement pc. - __asm__ __volatile__("nop"); - __builtin_trap(); + // to the trap instruction. We make the trap conditional and prevent inlining + // of the function to ensure that the compiler doesn't remove the `ret` + // instruction altogether. + // + // It's also important that the trap instruction isn't the first instruction + // in the function (which it isn't because of the branch) for other unwinders + // that also decrement pc. + if (do_trap) + __builtin_trap(); } int main(int, char**) { signal(SIGTRAP, signal_handler); signal(SIGILL, signal_handler); - crashing_leaf_func(); + crashing_leaf_func(1); return -2; } diff --git a/src/native/external/llvm-libunwind/test/unwind_scalable_vectors.pass.cpp b/src/native/external/llvm-libunwind/test/unwind_scalable_vectors.pass.cpp index 250e2c8fc7b1e..a5c5947c870fd 100644 --- a/src/native/external/llvm-libunwind/test/unwind_scalable_vectors.pass.cpp +++ b/src/native/external/llvm-libunwind/test/unwind_scalable_vectors.pass.cpp @@ -13,30 +13,8 @@ #include #include -// Check correct unwinding of frame with VLENB-sized objects (vector registers): -// 1. Save return address (ra) in temporary register. -// 2. Load VLENB (vector length in bytes) and substract it from current stack -// pointer (sp) - equivalent to one vector register on stack frame. -// 3. Set DWARF cannonical frame address (CFA) to "sp + vlenb" expresssion so it -// can be correctly unwinded. -// 4. Call stepper() function and check that 2 unwind steps are successful - -// from stepper() into foo() and from foo() into main(). -// 5. Restore stack pointer and return address. -__attribute__((naked)) static void foo() { - __asm__(".cfi_startproc\n" - "mv s0, ra\n" - "csrr s1, vlenb\n" - "sub sp, sp, s1\n" - "# .cfi_def_cfa_expression sp + vlenb\n" - ".cfi_escape 0x0f, 0x07, 0x72, 0x00, 0x92, 0xa2, 0x38, 0x00, 0x22\n" - "call stepper\n" - "add sp, sp, s1\n" - "mv ra, s0\n" - "ret\n" - ".cfi_endproc\n"); -} - -extern "C" void stepper() { +#ifdef __riscv_vector +__attribute__((noinline)) extern "C" void stepper() { unw_cursor_t cursor; unw_context_t uc; unw_getcontext(&uc); @@ -47,4 +25,16 @@ extern "C" void stepper() { assert(unw_step(&cursor) > 0); } +// Check correct unwinding of frame with VLENB-sized objects (vector registers). +__attribute__((noinline)) static void foo() { + __rvv_int32m1_t v; + asm volatile("" : "=vr"(v)); // Dummy inline asm to def v. + stepper(); // def-use of v has cross the function, so that + // will triger spill/reload to/from the stack. + asm volatile("" ::"vr"(v)); // Dummy inline asm to use v. +} + int main() { foo(); } +#else +int main() { return 0; } +#endif diff --git a/src/native/external/zlib-ng-version.txt b/src/native/external/zlib-ng-version.txt index 4d7d255740d9c..1b6fdae17b30c 100644 --- a/src/native/external/zlib-ng-version.txt +++ b/src/native/external/zlib-ng-version.txt @@ -3,4 +3,8 @@ v2.1.6 https://github.com/zlib-ng/zlib-ng/releases/tag/2.1.6 -We have removed the zlib-ng/docs/ and zlib-ng/test/ folders from our local copy as these files are not needed for our compilation. \ No newline at end of file +We have removed the following folders from our local copy as these files are not needed for our compilation: + +- zlib-ng/docs/ +- zlib-ng/test/ +- zlib-ng/arch/s390/self-hosted-builder/ diff --git a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.Dockerfile b/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.Dockerfile deleted file mode 100644 index 136eec77a58c1..0000000000000 --- a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -# Self-Hosted IBM Z Github Actions Runner. - -# Temporary image: amd64 dependencies. -FROM amd64/ubuntu:20.04 as ld-prefix -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get -y install ca-certificates libicu66 libssl1.1 - -# Main image. -FROM s390x/ubuntu:20.04 - -# Packages for zlib-ng testing. -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get -y install \ - clang-11 \ - cmake \ - curl \ - gcc \ - git \ - jq \ - libxml2-dev \ - libxslt-dev \ - llvm-11-tools \ - ninja-build \ - python-is-python3 \ - python3 \ - python3-dev \ - python3-pip - -# amd64 dependencies. -COPY --from=ld-prefix / /usr/x86_64-linux-gnu/ -RUN ln -fs ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /usr/x86_64-linux-gnu/lib64/ -RUN ln -fs /etc/resolv.conf /usr/x86_64-linux-gnu/etc/ -ENV QEMU_LD_PREFIX=/usr/x86_64-linux-gnu - -# amd64 Github Actions Runner. -RUN useradd -m actions-runner -USER actions-runner -WORKDIR /home/actions-runner -RUN curl -L https://github.com/actions/runner/releases/download/v2.287.1/actions-runner-linux-x64-2.287.1.tar.gz | tar -xz -VOLUME /home/actions-runner - -# Scripts. -COPY fs/ / -ENTRYPOINT ["/usr/bin/entrypoint"] -CMD ["/usr/bin/actions-runner"] diff --git a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.service b/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.service deleted file mode 100644 index 71053a79d6e7f..0000000000000 --- a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/actions-runner.service +++ /dev/null @@ -1,24 +0,0 @@ -[Unit] -Description=Self-Hosted IBM Z Github Actions Runner -Wants=qemu-user-static -After=qemu-user-static -StartLimitIntervalSec=0 - -[Service] -Type=simple -Restart=always -ExecStartPre=-/usr/bin/docker rm --force actions-runner -ExecStart=/usr/bin/docker run \ - --env-file=/etc/actions-runner \ - --init \ - --interactive \ - --name=actions-runner \ - --rm \ - --volume=actions-runner:/home/actions-runner \ - iiilinuxibmcom/actions-runner -ExecStop=/bin/sh -c "docker exec actions-runner kill -INT -- -1" -ExecStop=/bin/sh -c "docker wait actions-runner" -ExecStop=/bin/sh -c "docker rm actions-runner" - -[Install] -WantedBy=multi-user.target diff --git a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/qemu-user-static.service b/src/native/external/zlib-ng/arch/s390/self-hosted-builder/qemu-user-static.service deleted file mode 100644 index 301f3edd94c5e..0000000000000 --- a/src/native/external/zlib-ng/arch/s390/self-hosted-builder/qemu-user-static.service +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Support for transparent execution of non-native binaries with QEMU user emulation - -[Service] -Type=oneshot -# The source code for iiilinuxibmcom/qemu-user-static is at https://github.com/iii-i/qemu-user-static/tree/v6.1.0-1 -# TODO: replace it with multiarch/qemu-user-static once version >6.1 is available -ExecStart=/usr/bin/docker run --rm --interactive --privileged iiilinuxibmcom/qemu-user-static --reset -p yes - -[Install] -WantedBy=multi-user.target diff --git a/src/native/libs/Common/delayloadhook_windows.cpp b/src/native/libs/Common/delayloadhook_windows.cpp index 901ac5661749c..4e286cc69a586 100644 --- a/src/native/libs/Common/delayloadhook_windows.cpp +++ b/src/native/libs/Common/delayloadhook_windows.cpp @@ -18,7 +18,7 @@ FARPROC WINAPI secureDelayHook(unsigned dliNotify, PDelayLoadInfo pdli) return nullptr; } -// See https://docs.microsoft.com/en-us/cpp/build/reference/notification-hooks +// See https://learn.microsoft.com/cpp/build/reference/notification-hooks // This global hook is called prior to all the delay load LoadLibrary/GetProcAddress/etc. calls // Hooking this callback allows us to ensure that delay load LoadLibrary calls // specify the LOAD_LIBRARY_SEARCH_SYSTEM32 search path diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt index b847f5c3cd68b..a50ade7e86317 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -51,7 +51,7 @@ endif() add_custom_command( OUTPUT pal_swiftbindings.o - COMMAND xcrun swiftc -emit-object -static -parse-as-library -runtime-compatibility-version none -sdk ${CMAKE_OSX_SYSROOT} -target ${SWIFT_COMPILER_TARGET} ${CMAKE_CURRENT_SOURCE_DIR}/pal_swiftbindings.swift -o pal_swiftbindings.o + COMMAND xcrun swiftc -emit-object -static -parse-as-library -enable-library-evolution -g -runtime-compatibility-version none -sdk ${CMAKE_OSX_SYSROOT} -target ${SWIFT_COMPILER_TARGET} ${CMAKE_CURRENT_SOURCE_DIR}/pal_swiftbindings.swift -o pal_swiftbindings.o MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/pal_swiftbindings.swift COMMENT "Compiling Swift file pal_swiftbindings.swift" ) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c index 099fb34394711..41395f63b201f 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -48,6 +48,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_HmacFinal) DllImportEntry(AppleCryptoNative_HmacCurrent) DllImportEntry(AppleCryptoNative_HmacOneShot) + DllImportEntry(AppleCryptoNative_IsAuthenticationFailure) DllImportEntry(AppleCryptoNative_SecKeychainItemCopyKeychain) DllImportEntry(AppleCryptoNative_SecKeychainCopyDefault) DllImportEntry(AppleCryptoNative_SecKeychainCreate) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h index fd90fd0ad8216..058c94869b57d 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h @@ -10,3 +10,4 @@ EXTERN_C void* AppleCryptoNative_ChaCha20Poly1305Encrypt; EXTERN_C void* AppleCryptoNative_ChaCha20Poly1305Decrypt; EXTERN_C void* AppleCryptoNative_AesGcmEncrypt; EXTERN_C void* AppleCryptoNative_AesGcmDecrypt; +EXTERN_C void* AppleCryptoNative_IsAuthenticationFailure; diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 2a50deefb52bc..92d461886730b 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -4,166 +4,174 @@ import CryptoKit import Foundation -@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Encrypt") -public func AppleCryptoNative_ChaCha20Poly1305Encrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - plaintextPtr: UnsafeMutableRawPointer, - plaintextLength: Int32, - ciphertextBuffer: UnsafeMutablePointer, - ciphertextBufferLength: Int32, - tagBuffer: UnsafeMutablePointer, - tagBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 - ) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let plaintext = Data(bytesNoCopy: plaintextPtr, count: Int(plaintextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let symmetricKey = SymmetricKey(data: key) +protocol NonceProtocol { + init(data: D) throws where D : DataProtocol +} - guard let nonce = try? ChaChaPoly.Nonce(data: nonceData) else { - return 0 - } +protocol SealedBoxProtocol { + associatedtype Nonce : NonceProtocol - guard let result = try? ChaChaPoly.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) else { - return 0 - } + var ciphertext: Data { get } + var tag: Data { get } - assert(ciphertextBufferLength >= result.ciphertext.count) - assert(tagBufferLength >= result.tag.count) + init( + nonce: Nonce, + ciphertext: C, + tag: T + ) throws where C : DataProtocol, T : DataProtocol +} - result.ciphertext.copyBytes(to: ciphertextBuffer, count: result.ciphertext.count) - result.tag.copyBytes(to: tagBuffer, count: result.tag.count) - return 1 - } +protocol AEADSymmetricAlgorithm { + associatedtype SealedBox : SealedBoxProtocol + + static func seal(_ plaintext: Plaintext, using key: SymmetricKey, nonce: SealedBox.Nonce?) throws -> SealedBox where Plaintext: DataProtocol + static func seal<Plaintext, AuthenticatedData>(_ plaintext: Plaintext, using key: SymmetricKey, nonce: SealedBox.Nonce?, authenticating additionalData: AuthenticatedData) throws -> SealedBox where Plaintext: DataProtocol, AuthenticatedData: DataProtocol + static func open<AuthenticatedData>(_ sealedBox: SealedBox, using key: SymmetricKey, authenticating additionalData: AuthenticatedData) throws -> Data where AuthenticatedData: DataProtocol + static func open(_ sealedBox: SealedBox, using key: SymmetricKey) throws -> Data +} + +extension AES.GCM.Nonce: NonceProtocol {} +extension AES.GCM.SealedBox: SealedBoxProtocol { + typealias Nonce = AES.GCM.Nonce +} +extension AES.GCM: AEADSymmetricAlgorithm {} + +extension ChaChaPoly.Nonce: NonceProtocol {} +extension ChaChaPoly.SealedBox: SealedBoxProtocol { + typealias Nonce = ChaChaPoly.Nonce +} +extension ChaChaPoly: AEADSymmetricAlgorithm {} + +func encrypt<Algorithm>( + _ algorithm: Algorithm.Type, + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8>) throws where Algorithm: AEADSymmetricAlgorithm { -@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Decrypt") -public func AppleCryptoNative_ChaCha20Poly1305Decrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - ciphertextPtr: UnsafeMutableRawPointer, - ciphertextLength: Int32, - tagPtr: UnsafeMutableRawPointer, - tagLength: Int32, - plaintextBuffer: UnsafeMutablePointer<UInt8>, - plaintextBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 -) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let ciphertext = Data(bytesNoCopy: ciphertextPtr, count: Int(ciphertextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let tag = Data(bytesNoCopy: tagPtr, count: Int(tagLength), deallocator: Data.Deallocator.none) let symmetricKey = SymmetricKey(data: key) - guard let nonce = try? ChaChaPoly.Nonce(data: nonceData) else { - return 0 - } + let nonce = try Algorithm.SealedBox.Nonce(data: nonceData) - guard let sealedBox = try? ChaChaPoly.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { - return 0 - } + let result = try Algorithm.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) - do { - let result = try ChaChaPoly.open(sealedBox, using: symmetricKey, authenticating: aad) + // Copy results out of the SealedBox as the Data objects returned here are sometimes slices, + // which don't have a correct implementation of copyBytes. + // See https://github.com/apple/swift-foundation/issues/638 for more information. + let resultCiphertext = Data(result.ciphertext) + let resultTag = Data(result.tag) - assert(plaintextBufferLength >= result.count) - result.copyBytes(to: plaintextBuffer, count: result.count) - return 1 - } - catch CryptoKitError.authenticationFailure { - return -1 - } - catch { - return 0 - } + _ = resultCiphertext.copyBytes(to: cipherText) + _ = resultTag.copyBytes(to: tag) } -@_silgen_name("AppleCryptoNative_AesGcmEncrypt") -public func AppleCryptoNative_AesGcmEncrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - plaintextPtr: UnsafeMutableRawPointer, - plaintextLength: Int32, - ciphertextBuffer: UnsafeMutablePointer<UInt8>, - ciphertextBufferLength: Int32, - tagBuffer: UnsafeMutablePointer<UInt8>, - tagBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 - ) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let plaintext = Data(bytesNoCopy: plaintextPtr, count: Int(plaintextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) +func decrypt<Algorithm>( + _ algorithm: Algorithm.Type, + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8>) throws where Algorithm: AEADSymmetricAlgorithm { + let symmetricKey = SymmetricKey(data: key) - guard let nonce = try? AES.GCM.Nonce(data: nonceData) else { - return 0 - } + let nonce = try Algorithm.SealedBox.Nonce(data: nonceData) - guard let result = try? AES.GCM.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) else { - return 0 - } + let sealedBox = try Algorithm.SealedBox(nonce: nonce, ciphertext: cipherText, tag: tag) - assert(ciphertextBufferLength >= result.ciphertext.count) - assert(tagBufferLength >= result.tag.count) + let result = try Algorithm.open(sealedBox, using: symmetricKey, authenticating: aad) - result.ciphertext.copyBytes(to: ciphertextBuffer, count: result.ciphertext.count) - result.tag.copyBytes(to: tagBuffer, count: result.tag.count) - return 1 - } + _ = result.copyBytes(to: plaintext) +} -@_silgen_name("AppleCryptoNative_AesGcmDecrypt") -public func AppleCryptoNative_AesGcmDecrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - ciphertextPtr: UnsafeMutableRawPointer, - ciphertextLength: Int32, - tagPtr: UnsafeMutableRawPointer, - tagLength: Int32, - plaintextBuffer: UnsafeMutablePointer<UInt8>, - plaintextBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 -) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let ciphertext = Data(bytesNoCopy: ciphertextPtr, count: Int(ciphertextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let tag = Data(bytesNoCopy: tagPtr, count: Int(tagLength), deallocator: Data.Deallocator.none) - let symmetricKey = SymmetricKey(data: key) +@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Encrypt") +public func AppleCryptoNative_ChaCha20Poly1305Encrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try encrypt( + ChaChaPoly.self, + key: key, + nonceData: nonceData, + plaintext: plaintext, + cipherText: cipherText, + tag: tag, + aad: aad) + } - guard let nonce = try? AES.GCM.Nonce(data: nonceData) else { - return 0 - } +@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Decrypt") +public func AppleCryptoNative_ChaCha20Poly1305Decrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try decrypt( + ChaChaPoly.self, + key: key, + nonceData: nonceData, + cipherText: cipherText, + tag: tag, + plaintext: plaintext, + aad: aad); +} - guard let sealedBox = try? AES.GCM.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { - return 0 - } +@_silgen_name("AppleCryptoNative_AesGcmEncrypt") +public func AppleCryptoNative_AesGcmEncrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try encrypt( + AES.GCM.self, + key: key, + nonceData: nonceData, + plaintext: plaintext, + cipherText: cipherText, + tag: tag, + aad: aad) + } - do { - let result = try AES.GCM.open(sealedBox, using: symmetricKey, authenticating: aad) +@_silgen_name("AppleCryptoNative_AesGcmDecrypt") +public func AppleCryptoNative_AesGcmDecrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try decrypt( + AES.GCM.self, + key: key, + nonceData: nonceData, + cipherText: cipherText, + tag: tag, + plaintext: plaintext, + aad: aad); +} - assert(plaintextBufferLength >= result.count) - result.copyBytes(to: plaintextBuffer, count: result.count) - return 1 - } - catch CryptoKitError.authenticationFailure { - return -1 - } - catch { - return 0 +@_silgen_name("AppleCryptoNative_IsAuthenticationFailure") +public func AppleCryptoNative_IsAuthenticationFailure(error: Error) -> Bool { + if let error = error as? CryptoKitError { + switch error { + case .authenticationFailure: + return true + default: + return false + } } + return false } diff --git a/src/native/libs/System.Security.Cryptography.Native/extra_libs.cmake b/src/native/libs/System.Security.Cryptography.Native/extra_libs.cmake index 2af0ab2250467..1d7466e228318 100644 --- a/src/native/libs/System.Security.Cryptography.Native/extra_libs.cmake +++ b/src/native/libs/System.Security.Cryptography.Native/extra_libs.cmake @@ -4,26 +4,27 @@ macro(append_extra_cryptography_libs NativeLibsExtra) set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif(CMAKE_STATIC_LIB_LINK) + find_package(OpenSSL) # This is bad and wrong, but good enough to satisfy the build # We only care about having "enough" OpenSSL to get the native lib built # here, and it's on the end user to ship libssl/libcrypto from Google - if(FORCE_ANDROID_OPENSSL) - set(OPENSSL_CRYPTO_LIBRARY /usr/lib/x86_64-linux-gnu/libcrypto.so) - set(OPENSSL_SSL_LIBRARY /usr/lib/x86_64-linux-gnu/libssl.so) + if(FORCE_ANDROID_OPENSSL AND NOT OPENSSL_FOUND) + set(OPENSSL_CRYPTO_LIBRARY /usr/lib/x86_64-linux-gnu/libcrypto.so CACHE PATH "libcrypto.so" FORCE) + set(OPENSSL_SSL_LIBRARY /usr/lib/x86_64-linux-gnu/libssl.so CACHE PATH "libcrypto.so" FORCE) # Things get more wrong. We need Desktop OpenSSL headers, but # /usr/include is special cased and forbidden. We need to copy # the headers to a different location and use them - if(NOT DEFINED OPENSSL_INCLUDE_DIR) + if(NOT OPENSSL_INCLUDE_DIR) string(RANDOM LENGTH 24 _s) - set(OPENSSL_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${_s}/opensslheaders CACHE PATH "temporary directory") + set(OPENSSL_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${_s}/opensslheaders CACHE PATH "temporary directory" FORCE) file(MAKE_DIRECTORY ${OPENSSL_INCLUDE_DIR}) file(COPY /usr/include/openssl DESTINATION ${OPENSSL_INCLUDE_DIR}) file(GLOB_RECURSE opensslconf /usr/include/*/openssl/*conf*.h) file(COPY ${opensslconf} DESTINATION ${OPENSSL_INCLUDE_DIR}/openssl/) endif() + set(OPENSSL_FOUND TRUE CACHE BOOL "OpenSSL found" FORCE) endif() - find_package(OpenSSL) if(NOT OPENSSL_FOUND) message(FATAL_ERROR "!!! Cannot find libssl and System.Security.Cryptography.Native cannot build without it. Try installing libssl-dev (on Linux, but this may vary by distro) or openssl (on macOS) !!!. See the requirements document for your specific operating system: https://github.com/dotnet/runtime/tree/main/docs/workflow/requirements.") diff --git a/src/native/managed/cdacreader/src/Constants.cs b/src/native/managed/cdacreader/src/Constants.cs index a4874ce179e16..be4fd418b802d 100644 --- a/src/native/managed/cdacreader/src/Constants.cs +++ b/src/native/managed/cdacreader/src/Constants.cs @@ -9,6 +9,9 @@ internal static class Globals { // See src/coreclr/debug/runtimeinfo/datadescriptor.h internal const string ThreadStore = nameof(ThreadStore); + internal const string FinalizerThread = nameof(FinalizerThread); + internal const string GCThread = nameof(GCThread); + internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion); } } diff --git a/src/native/managed/cdacreader/src/Contracts/Thread.cs b/src/native/managed/cdacreader/src/Contracts/Thread.cs index 30187567fe561..58621f005d437 100644 --- a/src/native/managed/cdacreader/src/Contracts/Thread.cs +++ b/src/native/managed/cdacreader/src/Contracts/Thread.cs @@ -5,17 +5,29 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; -// TODO: [cdac] Add other counts / threads internal record struct ThreadStoreData( int ThreadCount, - TargetPointer FirstThread); + TargetPointer FirstThread, + TargetPointer FinalizerThread, + TargetPointer GCThread); + +internal record struct ThreadStoreCounts( + int UnstartedThreadCount, + int BackgroundThreadCount, + int PendingThreadCount, + int DeadThreadCount); + +internal record struct ThreadData( + uint Id, + TargetPointer NextThread); internal interface IThread : IContract { static string IContract.Name { get; } = nameof(Thread); static IContract IContract.Create(Target target, int version) { - TargetPointer threadStore = target.ReadGlobalPointer(Constants.Globals.ThreadStore); + TargetPointer threadStorePointer = target.ReadGlobalPointer(Constants.Globals.ThreadStore); + TargetPointer threadStore = target.ReadPointer(threadStorePointer); return version switch { 1 => new Thread_1(target, threadStore), @@ -24,6 +36,8 @@ static IContract IContract.Create(Target target, int version) } public virtual ThreadStoreData GetThreadStoreData() => throw new NotImplementedException(); + public virtual ThreadStoreCounts GetThreadCounts() => throw new NotImplementedException(); + public virtual ThreadData GetThreadData(TargetPointer thread) => throw new NotImplementedException(); } internal readonly struct Thread : IThread @@ -35,24 +49,53 @@ static IContract IContract.Create(Target target, int version) { private readonly Target _target; private readonly TargetPointer _threadStoreAddr; + private readonly ulong _threadLinkOffset; internal Thread_1(Target target, TargetPointer threadStore) { _target = target; _threadStoreAddr = threadStore; + + // Get the offset into Thread of the SLink. We use this to find the actual + // first thread from the linked list node contained by the first thread. + Target.TypeInfo type = _target.GetTypeInfo(DataType.Thread); + _threadLinkOffset = (ulong)type.Fields[nameof(Data.Thread.LinkNext)].Offset; } ThreadStoreData IThread.GetThreadStoreData() { - Data.ThreadStore? threadStore; - if (!_target.ProcessedData.TryGet(_threadStoreAddr.Value, out threadStore)) - { - threadStore = new Data.ThreadStore(_target, _threadStoreAddr); + Data.ThreadStore threadStore = _target.ProcessedData.GetOrAdd<Data.ThreadStore>(_threadStoreAddr); + return new ThreadStoreData( + threadStore.ThreadCount, + GetThreadFromLink(threadStore.FirstThreadLink), + _target.ReadGlobalPointer(Constants.Globals.FinalizerThread), + _target.ReadGlobalPointer(Constants.Globals.GCThread)); + } + + ThreadStoreCounts IThread.GetThreadCounts() + { + Data.ThreadStore threadStore = _target.ProcessedData.GetOrAdd<Data.ThreadStore>(_threadStoreAddr); + return new ThreadStoreCounts( + threadStore.UnstartedCount, + threadStore.BackgroundCount, + threadStore.PendingCount, + threadStore.DeadCount); + } + + ThreadData IThread.GetThreadData(TargetPointer threadPointer) + { + Data.Thread thread = _target.ProcessedData.GetOrAdd<Data.Thread>(threadPointer); + return new ThreadData( + thread.Id, + GetThreadFromLink(thread.LinkNext)); + } - // Still okay if processed data is already registered by someone else - _ = _target.ProcessedData.TryRegister(_threadStoreAddr.Value, threadStore); - } + private TargetPointer GetThreadFromLink(TargetPointer threadLink) + { + if (threadLink == TargetPointer.Null) + return TargetPointer.Null; - return new ThreadStoreData(threadStore.ThreadCount, threadStore.FirstThread); + // Get the address of the thread containing the link + return new TargetPointer(threadLink - _threadLinkOffset); } } diff --git a/src/native/managed/cdacreader/src/Data/IData.cs b/src/native/managed/cdacreader/src/Data/IData.cs new file mode 100644 index 0000000000000..65b7be805c838 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/IData.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal interface IData<TSelf> where TSelf : IData<TSelf> +{ + static abstract TSelf Create(Target target, TargetPointer address); +} diff --git a/src/native/managed/cdacreader/src/Data/Thread.cs b/src/native/managed/cdacreader/src/Data/Thread.cs new file mode 100644 index 0000000000000..3f29b90126a36 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/Thread.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class Thread : IData<Thread> +{ + static Thread IData<Thread>.Create(Target target, TargetPointer address) + => new Thread(target, address); + + public Thread(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.Thread); + + Id = target.Read<uint>(address + (ulong)type.Fields[nameof(Id)].Offset); + LinkNext = target.ReadPointer(address + (ulong)type.Fields[nameof(LinkNext)].Offset); + } + + public uint Id { get; init; } + public TargetPointer LinkNext { get; init; } +} diff --git a/src/native/managed/cdacreader/src/Data/ThreadStore.cs b/src/native/managed/cdacreader/src/Data/ThreadStore.cs index 1ba13596359b5..afef1e835be32 100644 --- a/src/native/managed/cdacreader/src/Data/ThreadStore.cs +++ b/src/native/managed/cdacreader/src/Data/ThreadStore.cs @@ -3,18 +3,27 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; -internal sealed class ThreadStore +internal sealed class ThreadStore : IData<ThreadStore> { - public ThreadStore(Target target, TargetPointer pointer) + static ThreadStore IData<ThreadStore>.Create(Target target, TargetPointer address) + => new ThreadStore(target, address); + + public ThreadStore(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.ThreadStore); - TargetPointer addr = target.ReadPointer(pointer.Value); - ThreadCount = target.Read<int>(addr.Value + (ulong)type.Fields[nameof(ThreadCount)].Offset); - FirstThread = TargetPointer.Null; + ThreadCount = target.Read<int>(address + (ulong)type.Fields[nameof(ThreadCount)].Offset); + FirstThreadLink = target.ReadPointer(address + (ulong)type.Fields[nameof(FirstThreadLink)].Offset); + UnstartedCount = target.Read<int>(address + (ulong)type.Fields[nameof(UnstartedCount)].Offset); + BackgroundCount = target.Read<int>(address + (ulong)type.Fields[nameof(BackgroundCount)].Offset); + PendingCount = target.Read<int>(address + (ulong)type.Fields[nameof(PendingCount)].Offset); + DeadCount = target.Read<int>(address + (ulong)type.Fields[nameof(DeadCount)].Offset); } public int ThreadCount { get; init; } - - public TargetPointer FirstThread { get; init; } + public TargetPointer FirstThreadLink { get; init; } + public int UnstartedCount { get; init; } + public int BackgroundCount { get; init; } + public int PendingCount { get; init; } + public int DeadCount { get; init; } } diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 5cea71a68b8a9..04fe0848c0395 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -11,6 +11,11 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; /// Implementation of ISOSDacInterface* interfaces intended to be passed out to consumers /// interacting with the DAC via those COM interfaces. /// </summary> +/// <remarks> +/// Functions on <see cref="ISOSDacInterface"/> are defined with PreserveSig. Target and Contracts +/// throw on errors. Implementations in this class should wrap logic in a try-catch and return the +/// corresponding error code. +/// </remarks> [GeneratedComClass] internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9 { @@ -102,7 +107,24 @@ public int GetBreakingChangeVersion() public unsafe int GetSyncBlockCleanupData(ulong addr, void* data) => HResults.E_NOTIMPL; public unsafe int GetSyncBlockData(uint number, void* data) => HResults.E_NOTIMPL; public unsafe int GetThreadAllocData(ulong thread, void* data) => HResults.E_NOTIMPL; - public unsafe int GetThreadData(ulong thread, DacpThreadData* data) => HResults.E_NOTIMPL; + + public unsafe int GetThreadData(ulong thread, DacpThreadData* data) + { + try + { + Contracts.IThread contract = _target.Contracts.Thread; + Contracts.ThreadData threadData = contract.GetThreadData(thread); + data->corThreadId = (int)threadData.Id; + data->nextThread = threadData.NextThread; + } + catch (Exception ex) + { + return ex.HResult; + } + + // TODO: [cdac] Implement/populate rest of thread data fields + return HResults.E_NOTIMPL; + } public unsafe int GetThreadFromThinlockID(uint thinLockId, ulong* pThread) => HResults.E_NOTIMPL; public unsafe int GetThreadLocalModuleData(ulong thread, uint index, void* data) => HResults.E_NOTIMPL; public unsafe int GetThreadpoolData(void* data) => HResults.E_NOTIMPL; @@ -114,15 +136,24 @@ public unsafe int GetThreadStoreData(DacpThreadStoreData* data) Contracts.IThread thread = _target.Contracts.Thread; Contracts.ThreadStoreData threadStoreData = thread.GetThreadStoreData(); data->threadCount = threadStoreData.ThreadCount; - data->firstThread = threadStoreData.FirstThread.Value; - data->fHostConfig = 0; + data->firstThread = threadStoreData.FirstThread; + data->finalizerThread = threadStoreData.FinalizerThread; + data->gcThread = threadStoreData.GCThread; + + Contracts.ThreadStoreCounts threadCounts = thread.GetThreadCounts(); + data->unstartedThreadCount = threadCounts.UnstartedThreadCount; + data->backgroundThreadCount = threadCounts.BackgroundThreadCount; + data->pendingThreadCount = threadCounts.PendingThreadCount; + data->deadThreadCount = threadCounts.DeadThreadCount; + + data->fHostConfig = 0; // Always 0 for non-Framework } catch (Exception ex) { return ex.HResult; } - return HResults.E_NOTIMPL; + return HResults.S_OK; } public unsafe int GetTLSIndex(uint* pIndex) => HResults.E_NOTIMPL; diff --git a/src/native/managed/cdacreader/src/Target.cs b/src/native/managed/cdacreader/src/Target.cs index 0202efaea38ed..83f637034dc64 100644 --- a/src/native/managed/cdacreader/src/Target.cs +++ b/src/native/managed/cdacreader/src/Target.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; +using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader; @@ -15,8 +17,20 @@ public struct TargetPointer public ulong Value; public TargetPointer(ulong value) => Value = value; + + public static implicit operator ulong(TargetPointer p) => p.Value; + public static implicit operator TargetPointer(ulong v) => new TargetPointer(v); } +/// <summary> +/// Representation of the target under inspection +/// </summary> +/// <remarks> +/// This class provides APIs used by contracts for reading from the target and getting type and globals +/// information based on the target's contract descriptor. Like the contracts themselves in cdacreader, +/// these are throwing APIs. Any callers at the boundaries (for example, unmanaged entry points, COM) +/// should handle any exceptions. +/// </remarks> public sealed unsafe class Target { public record struct TypeInfo @@ -51,7 +65,7 @@ private readonly struct Configuration private readonly Dictionary<string, TypeInfo> _types = []; internal Contracts.Registry Contracts { get; } - internal DataCache ProcessedData { get; } = new DataCache(); + internal DataCache ProcessedData { get; } public static bool TryCreate(ulong contractDescriptor, delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget, void* readContext, out Target? target) { @@ -69,6 +83,7 @@ public static bool TryCreate(ulong contractDescriptor, delegate* unmanaged<ulong private Target(Configuration config, ContractDescriptorParser.ContractDescriptor descriptor, TargetPointer[] pointerData, Reader reader) { Contracts = new Contracts.Registry(this); + ProcessedData = new DataCache(this); _config = config; _reader = reader; @@ -328,14 +343,29 @@ internal bool TryGetContractVersion(string contractName, out int version) /// </summary> internal sealed class DataCache { + private readonly Target _target; private readonly Dictionary<(ulong, Type), object?> _readDataByAddress = []; - public bool TryRegister<T>(ulong address, T data) + public DataCache(Target target) + { + _target = target; + } + + public T GetOrAdd<T>(TargetPointer address) where T : IData<T> { - return _readDataByAddress.TryAdd((address, typeof(T)), data); + if (TryGet(address, out T? result)) + return result; + + T constructed = T.Create(_target, address); + if (_readDataByAddress.TryAdd((address, typeof(T)), constructed)) + return constructed; + + bool found = TryGet(address, out result); + Debug.Assert(found); + return result!; } - public bool TryGet<T>(ulong address, [NotNullWhen(true)] out T? data) + private bool TryGet<T>(ulong address, [NotNullWhen(true)] out T? data) { data = default; if (!_readDataByAddress.TryGetValue((address, typeof(T)), out object? dataObj)) diff --git a/src/native/minipal/cpufeatures.c b/src/native/minipal/cpufeatures.c index a2ff83222140c..1af0f86f18243 100644 --- a/src/native/minipal/cpufeatures.c +++ b/src/native/minipal/cpufeatures.c @@ -6,6 +6,7 @@ #include <stddef.h> #include <stdio.h> #include <string.h> +#include <assert.h> #include "cpufeatures.h" #include "cpuid.h" @@ -154,159 +155,150 @@ int minipal_getcpufeatures(void) __cpuid(cpuidInfo, 0x00000000); uint32_t maxCpuId = (uint32_t)cpuidInfo[CPUID_EAX]; + assert(maxCpuId >= 1); - if (maxCpuId >= 1) - { - __cpuid(cpuidInfo, 0x00000001); + __cpuid(cpuidInfo, 0x00000001); - const int requiredBaselineEdxFlags = (1 << 25) // SSE - | (1 << 26); // SSE2 + assert((cpuidInfo[CPUID_EDX] & (1 << 25)) != 0); // SSE + assert((cpuidInfo[CPUID_EDX] & (1 << 26)) != 0); // SSE2 - if ((cpuidInfo[CPUID_EDX] & requiredBaselineEdxFlags) == requiredBaselineEdxFlags) - { - result |= XArchIntrinsicConstants_VectorT128; + if ((cpuidInfo[CPUID_ECX] & (1 << 25)) != 0) // AESNI + { + result |= XArchIntrinsicConstants_Aes; + } - if ((cpuidInfo[CPUID_ECX] & (1 << 25)) != 0) // AESNI - { - result |= XArchIntrinsicConstants_Aes; - } + if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0) // PCLMULQDQ + { + result |= XArchIntrinsicConstants_Pclmulqdq; + } - if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0) // PCLMULQDQ - { - result |= XArchIntrinsicConstants_Pclmulqdq; - } + if ((cpuidInfo[CPUID_ECX] & (1 << 0)) != 0) // SSE3 + { + result |= XArchIntrinsicConstants_Sse3; + + if ((cpuidInfo[CPUID_ECX] & (1 << 9)) != 0) // SSSE3 + { + result |= XArchIntrinsicConstants_Ssse3; - if ((cpuidInfo[CPUID_ECX] & (1 << 0)) != 0) // SSE3 + if ((cpuidInfo[CPUID_ECX] & (1 << 19)) != 0) // SSE4.1 { - result |= XArchIntrinsicConstants_Sse3; + result |= XArchIntrinsicConstants_Sse41; - if ((cpuidInfo[CPUID_ECX] & (1 << 9)) != 0) // SSSE3 + if ((cpuidInfo[CPUID_ECX] & (1 << 20)) != 0) // SSE4.2 { - result |= XArchIntrinsicConstants_Ssse3; + result |= XArchIntrinsicConstants_Sse42; - if ((cpuidInfo[CPUID_ECX] & (1 << 19)) != 0) // SSE4.1 + if ((cpuidInfo[CPUID_ECX] & (1 << 22)) != 0) // MOVBE { - result |= XArchIntrinsicConstants_Sse41; + result |= XArchIntrinsicConstants_Movbe; + } + + if ((cpuidInfo[CPUID_ECX] & (1 << 23)) != 0) // POPCNT + { + result |= XArchIntrinsicConstants_Popcnt; + } - if ((cpuidInfo[CPUID_ECX] & (1 << 20)) != 0) // SSE4.2 + const int requiredAvxEcxFlags = (1 << 27) // OSXSAVE + | (1 << 28); // AVX + + if ((cpuidInfo[CPUID_ECX] & requiredAvxEcxFlags) == requiredAvxEcxFlags) + { + if (IsAvxEnabled() && (xmmYmmStateSupport() == 1)) // XGETBV == 11 { - result |= XArchIntrinsicConstants_Sse42; + result |= XArchIntrinsicConstants_Avx; - if ((cpuidInfo[CPUID_ECX] & (1 << 22)) != 0) // MOVBE + if ((cpuidInfo[CPUID_ECX] & (1 << 12)) != 0) // FMA { - result |= XArchIntrinsicConstants_Movbe; + result |= XArchIntrinsicConstants_Fma; } - if ((cpuidInfo[CPUID_ECX] & (1 << 23)) != 0) // POPCNT + if (maxCpuId >= 0x07) { - result |= XArchIntrinsicConstants_Popcnt; - } - - const int requiredAvxEcxFlags = (1 << 27) // OSXSAVE - | (1 << 28); // AVX + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); - if ((cpuidInfo[CPUID_ECX] & requiredAvxEcxFlags) == requiredAvxEcxFlags) - { - if (IsAvxEnabled() && (xmmYmmStateSupport() == 1)) // XGETBV == 11 + if ((cpuidInfo[CPUID_EBX] & (1 << 5)) != 0) // AVX2 { - result |= XArchIntrinsicConstants_Avx; - - if ((cpuidInfo[CPUID_ECX] & (1 << 12)) != 0) // FMA - { - result |= XArchIntrinsicConstants_Fma; - } + result |= XArchIntrinsicConstants_Avx2; - if (maxCpuId >= 0x07) + if (IsAvx512Enabled() && (avx512StateSupport() == 1)) // XGETBV XRC0[7:5] == 111 { - __cpuidex(cpuidInfo, 0x00000007, 0x00000000); - - if ((cpuidInfo[CPUID_EBX] & (1 << 5)) != 0) // AVX2 + if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) // AVX512F { - result |= XArchIntrinsicConstants_Avx2; - result |= XArchIntrinsicConstants_VectorT256; + result |= XArchIntrinsicConstants_Avx512f; + + bool isAVX512_VLSupported = false; + if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0) // AVX512VL + { + result |= XArchIntrinsicConstants_Avx512f_vl; + isAVX512_VLSupported = true; + } - if (IsAvx512Enabled() && (avx512StateSupport() == 1)) // XGETBV XRC0[7:5] == 111 + if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0) // AVX512BW { - if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) // AVX512F + result |= XArchIntrinsicConstants_Avx512bw; + if (isAVX512_VLSupported) // AVX512BW_VL { - result |= XArchIntrinsicConstants_Avx512f; - result |= XArchIntrinsicConstants_VectorT512; - - bool isAVX512_VLSupported = false; - if ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0) // AVX512VL - { - result |= XArchIntrinsicConstants_Avx512f_vl; - isAVX512_VLSupported = true; - } - - if ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0) // AVX512BW - { - result |= XArchIntrinsicConstants_Avx512bw; - if (isAVX512_VLSupported) // AVX512BW_VL - { - result |= XArchIntrinsicConstants_Avx512bw_vl; - } - } - - if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0) // AVX512CD - { - result |= XArchIntrinsicConstants_Avx512cd; - if (isAVX512_VLSupported) // AVX512CD_VL - { - result |= XArchIntrinsicConstants_Avx512cd_vl; - } - } - - if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) // AVX512DQ - { - result |= XArchIntrinsicConstants_Avx512dq; - if (isAVX512_VLSupported) // AVX512DQ_VL - { - result |= XArchIntrinsicConstants_Avx512dq_vl; - } - } - - if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0) // AVX512VBMI - { - result |= XArchIntrinsicConstants_Avx512Vbmi; - if (isAVX512_VLSupported) // AVX512VBMI_VL - { - result |= XArchIntrinsicConstants_Avx512Vbmi_vl; - } - } + result |= XArchIntrinsicConstants_Avx512bw_vl; } } - __cpuidex(cpuidInfo, 0x00000007, 0x00000001); + if ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0) // AVX512CD + { + result |= XArchIntrinsicConstants_Avx512cd; + if (isAVX512_VLSupported) // AVX512CD_VL + { + result |= XArchIntrinsicConstants_Avx512cd_vl; + } + } - if ((cpuidInfo[CPUID_EAX] & (1 << 4)) != 0) // AVX-VNNI + if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) // AVX512DQ { - result |= XArchIntrinsicConstants_AvxVnni; + result |= XArchIntrinsicConstants_Avx512dq; + if (isAVX512_VLSupported) // AVX512DQ_VL + { + result |= XArchIntrinsicConstants_Avx512dq_vl; + } } - if ((cpuidInfo[CPUID_EDX] & (1 << 19)) != 0) // Avx10 + if ((cpuidInfo[CPUID_ECX] & (1 << 1)) != 0) // AVX512VBMI { - __cpuidex(cpuidInfo, 0x00000024, 0x00000000); - if((cpuidInfo[CPUID_EBX] & 0xFF) >= 1) // Avx10v1 - CPUID.(EAX=24H, ECX=00H):EBX[7:0] >= 1 + result |= XArchIntrinsicConstants_Avx512Vbmi; + if (isAVX512_VLSupported) // AVX512VBMI_VL { - if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) - { - result |= XArchIntrinsicConstants_Avx10v1; - } - - if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) - { - result |= XArchIntrinsicConstants_Avx10v1_V256; - } - - if ((cpuidInfo[CPUID_EBX] & (1 << 18)) != 0) - { - result |= XArchIntrinsicConstants_Avx10v1_V512; - } + result |= XArchIntrinsicConstants_Avx512Vbmi_vl; } } } } + + __cpuidex(cpuidInfo, 0x00000007, 0x00000001); + + if ((cpuidInfo[CPUID_EAX] & (1 << 4)) != 0) // AVX-VNNI + { + result |= XArchIntrinsicConstants_AvxVnni; + } + + if ((cpuidInfo[CPUID_EDX] & (1 << 19)) != 0) // Avx10 + { + __cpuidex(cpuidInfo, 0x00000024, 0x00000000); + if((cpuidInfo[CPUID_EBX] & 0xFF) >= 1) // Avx10v1 - CPUID.(EAX=24H, ECX=00H):EBX[7:0] >= 1 + { + if ((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) + { + result |= XArchIntrinsicConstants_Avx10v1; + } + + if ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) + { + result |= XArchIntrinsicConstants_Avx10v1_V256; + } + + if ((cpuidInfo[CPUID_EBX] & (1 << 18)) != 0) + { + result |= XArchIntrinsicConstants_Avx10v1_V512; + } + } + } } } } @@ -314,25 +306,25 @@ int minipal_getcpufeatures(void) } } } + } - if (maxCpuId >= 0x07) - { - __cpuidex(cpuidInfo, 0x00000007, 0x00000000); + if (maxCpuId >= 0x07) + { + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); - if ((cpuidInfo[CPUID_EBX] & (1 << 3)) != 0) // BMI1 - { - result |= XArchIntrinsicConstants_Bmi1; - } + if ((cpuidInfo[CPUID_EBX] & (1 << 3)) != 0) // BMI1 + { + result |= XArchIntrinsicConstants_Bmi1; + } - if ((cpuidInfo[CPUID_EBX] & (1 << 8)) != 0) // BMI2 - { - result |= XArchIntrinsicConstants_Bmi2; - } + if ((cpuidInfo[CPUID_EBX] & (1 << 8)) != 0) // BMI2 + { + result |= XArchIntrinsicConstants_Bmi2; + } - if ((cpuidInfo[CPUID_EDX] & (1 << 14)) != 0) - { - result |= XArchIntrinsicConstants_Serialize; // SERIALIZE - } + if ((cpuidInfo[CPUID_EDX] & (1 << 14)) != 0) + { + result |= XArchIntrinsicConstants_Serialize; // SERIALIZE } } @@ -382,7 +374,7 @@ int minipal_getcpufeatures(void) result |= ARM64IntrinsicConstants_Sha256; if (hwCap & HWCAP_ASIMD) - result |= ARM64IntrinsicConstants_AdvSimd | ARM64IntrinsicConstants_VectorT128; + result |= ARM64IntrinsicConstants_AdvSimd; if (hwCap & HWCAP_ASIMDRDM) result |= ARM64IntrinsicConstants_Rdm; @@ -427,13 +419,13 @@ int minipal_getcpufeatures(void) // Every ARM64 CPU should support SIMD and FP // If the OS have no function to query for CPU capabilities we set just these - result |= ARM64IntrinsicConstants_AdvSimd | ARM64IntrinsicConstants_VectorT128; + result |= ARM64IntrinsicConstants_AdvSimd; #endif // HAVE_AUXV_HWCAP_H #endif // HOST_UNIX #if defined(HOST_WINDOWS) // FP and SIMD support are enabled by default - result |= ARM64IntrinsicConstants_AdvSimd | ARM64IntrinsicConstants_VectorT128; + result |= ARM64IntrinsicConstants_AdvSimd; if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) { diff --git a/src/native/minipal/cpufeatures.h b/src/native/minipal/cpufeatures.h index 472ce17833961..62aa1c75256a8 100644 --- a/src/native/minipal/cpufeatures.h +++ b/src/native/minipal/cpufeatures.h @@ -37,12 +37,9 @@ enum XArchIntrinsicConstants XArchIntrinsicConstants_Avx512Vbmi = 0x800000, XArchIntrinsicConstants_Avx512Vbmi_vl = 0x1000000, XArchIntrinsicConstants_Serialize = 0x2000000, - XArchIntrinsicConstants_VectorT128 = 0x4000000, - XArchIntrinsicConstants_VectorT256 = 0x8000000, - XArchIntrinsicConstants_VectorT512 = 0x10000000, - XArchIntrinsicConstants_Avx10v1 = 0x20000000, - XArchIntrinsicConstants_Avx10v1_V256 = 0x40000000, - XArchIntrinsicConstants_Avx10v1_V512 = 0x80000000, + XArchIntrinsicConstants_Avx10v1 = 0x4000000, + XArchIntrinsicConstants_Avx10v1_V256 = 0x8000000, + XArchIntrinsicConstants_Avx10v1_V512 = 0x10000000, }; #endif // HOST_X86 || HOST_AMD64 @@ -58,9 +55,8 @@ enum ARM64IntrinsicConstants ARM64IntrinsicConstants_Sha256 = 0x0040, ARM64IntrinsicConstants_Atomics = 0x0080, ARM64IntrinsicConstants_Rcpc = 0x0100, - ARM64IntrinsicConstants_VectorT128 = 0x0200, - ARM64IntrinsicConstants_Rcpc2 = 0x0400, - ARM64IntrinsicConstants_Sve = 0x0800, + ARM64IntrinsicConstants_Rcpc2 = 0x0200, + ARM64IntrinsicConstants_Sve = 0x0400, }; #include <assert.h> diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs index 2d075541db17b..d146613f16f44 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.NET.Sdk.WebAssembly { - public class BootJsonBuilderHelper(TaskLoggingHelper Log, bool IsMultiThreaded) + public class BootJsonBuilderHelper(TaskLoggingHelper Log, string DebugLevel, bool IsMultiThreaded, bool IsPublish) { private static readonly string[] coreAssemblyNames = [ "System.Private.CoreLib", @@ -106,5 +106,35 @@ static void AddDictionary(StringBuilder sb, Dictionary<string, string>? res) return null; } + + public int GetDebugLevel(bool hasPdb) + { + int? debugLevel = ParseOptionalInt(DebugLevel); + + // If user didn't give us a value, check if we have any PDB. + if (debugLevel == null && hasPdb) + debugLevel = -1; + + // Fallback to -1 for build, or 0 for publish + debugLevel ??= IsPublish ? 0 : -1; + + return debugLevel.Value; + } + + public bool? ParseOptionalBool(string value) + { + if (string.IsNullOrEmpty(value) || !bool.TryParse(value, out var boolValue)) + return null; + + return boolValue; + } + + public int? ParseOptionalInt(string value) + { + if (string.IsNullOrEmpty(value) || !int.TryParse(value, out var intValue)) + return null; + + return intValue; + } } } diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs index fbe0aa7e3733e..33f3debdd4ef8 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs @@ -101,12 +101,12 @@ public override bool Execute() // Internal for tests public void WriteBootJson(Stream output, string entryAssemblyName) { - var helper = new BootJsonBuilderHelper(Log, IsMultiThreaded); + var helper = new BootJsonBuilderHelper(Log, DebugLevel, IsMultiThreaded, IsPublish); var result = new BootJsonData { resources = new ResourcesData(), - startupMemoryCache = ParseOptionalBool(StartupMemoryCache), + startupMemoryCache = helper.ParseOptionalBool(StartupMemoryCache), }; if (IsTargeting80OrLater()) @@ -136,7 +136,7 @@ public void WriteBootJson(Stream output, string entryAssemblyName) result.runtimeOptions = runtimeOptions; } - bool? jiterpreter = ParseOptionalBool(Jiterpreter); + bool? jiterpreter = helper.ParseOptionalBool(Jiterpreter); if (jiterpreter != null) { var runtimeOptions = result.runtimeOptions?.ToHashSet() ?? new HashSet<string>(3); @@ -357,16 +357,7 @@ public void WriteBootJson(Stream output, string entryAssemblyName) if (IsTargeting80OrLater()) { - int? debugLevel = ParseOptionalInt(DebugLevel); - - // If user didn't give us a value, check if we have any PDB. - if (debugLevel == null && result.resources?.pdb?.Count > 0) - debugLevel = -1; - - // Fallback to -1 for build, or 0 for publish - debugLevel ??= IsPublish ? 0 : -1; - - result.debugLevel = debugLevel.Value; + result.debugLevel = helper.GetDebugLevel(result.resources?.pdb?.Count > 0); } if (ConfigurationFiles != null) @@ -434,22 +425,6 @@ private GlobalizationMode GetGlobalizationMode() return GlobalizationMode.Sharded; } - private static bool? ParseOptionalBool(string value) - { - if (string.IsNullOrEmpty(value) || !bool.TryParse(value, out var boolValue)) - return null; - - return boolValue; - } - - private static int? ParseOptionalInt(string value) - { - if (string.IsNullOrEmpty(value) || !int.TryParse(value, out var intValue)) - return null; - - return intValue; - } - private void AddToAdditionalResources(ITaskItem resource, Dictionary<string, AdditionalAsset> additionalResources, string resourceName, string behavior) { if (!additionalResources.ContainsKey(resourceName)) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index b16c21e117fd7..9fca78e4263fa 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -30,7 +30,8 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask public string? WasmIcuDataFileName { get; set; } public string? RuntimeAssetsLocation { get; set; } public bool CacheBootResources { get; set; } - public int DebugLevel { get; set; } + public string? DebugLevel { get; set; } + public bool IsPublish { get; set; } public bool IsAot { get; set; } public bool IsMultiThreaded { get; set; } @@ -97,7 +98,7 @@ private GlobalizationMode GetGlobalizationMode() protected override bool ExecuteInternal() { - var helper = new BootJsonBuilderHelper(Log, IsMultiThreaded); + var helper = new BootJsonBuilderHelper(Log, DebugLevel!, IsMultiThreaded, IsPublish); var logAdapter = new LogAdapter(Log); if (!ValidateArguments()) @@ -133,6 +134,8 @@ protected override bool ExecuteInternal() if (UseWebcil) Log.LogMessage(MessageImportance.Normal, "Converting assemblies to Webcil"); + int baseDebugLevel = helper.GetDebugLevel(false); + foreach (var assembly in _assemblies) { if (UseWebcil) @@ -151,7 +154,7 @@ protected override bool ExecuteInternal() { FileCopyChecked(assembly, Path.Combine(runtimeAssetsPath, Path.GetFileName(assembly)), "Assemblies"); } - if (DebugLevel != 0) + if (baseDebugLevel != 0) { var pdb = assembly; pdb = Path.ChangeExtension(pdb, ".pdb"); @@ -217,7 +220,7 @@ protected override bool ExecuteInternal() var assemblyList = isCoreAssembly ? bootConfig.resources.coreAssembly : bootConfig.resources.assembly; assemblyList[assemblyName] = Utils.ComputeIntegrity(bytes); - if (DebugLevel != 0) + if (baseDebugLevel != 0) { var pdb = Path.ChangeExtension(assembly, ".pdb"); if (File.Exists(pdb)) @@ -240,7 +243,7 @@ protected override bool ExecuteInternal() } } - bootConfig.debugLevel = DebugLevel; + bootConfig.debugLevel = helper.GetDebugLevel(bootConfig.resources.pdb?.Count > 0); ProcessSatelliteAssemblies(args => { diff --git a/src/tasks/WorkloadBuildTasks/PatchNuGetConfig.cs b/src/tasks/WorkloadBuildTasks/PatchNuGetConfig.cs index 34dfe2c1706ee..a4828b1b3bed1 100644 --- a/src/tasks/WorkloadBuildTasks/PatchNuGetConfig.cs +++ b/src/tasks/WorkloadBuildTasks/PatchNuGetConfig.cs @@ -44,7 +44,7 @@ public class PatchNuGetConfig : Task * <package pattern="Foo*" /> * </packageSource> * - * This is useful when using Central Package Management (https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management) + * This is useful when using Central Package Management (https://learn.microsoft.com/nuget/consume-packages/central-package-management) */ public string[] NuGetConfigPackageSourceMappings { get; set; } = Array.Empty<string>(); diff --git a/src/tests/Common/CoreCLRTestLibrary/Utilities.cs b/src/tests/Common/CoreCLRTestLibrary/Utilities.cs index a3d6df5a62348..feadf19dc379d 100644 --- a/src/tests/Common/CoreCLRTestLibrary/Utilities.cs +++ b/src/tests/Common/CoreCLRTestLibrary/Utilities.cs @@ -340,7 +340,7 @@ private sealed class Kernel32 public const int PRODUCT_HOME_PREMIUM_N = 0x0000001A; /// <summary> - /// https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getproductinfo + /// https://learn.microsoft.com/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getproductinfo /// </summary> [DllImport(nameof(Kernel32), SetLastError = false)] public static extern bool GetProductInfo( diff --git a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs index 69f3a302f1d4a..826ed224cadf3 100644 --- a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs +++ b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs @@ -305,7 +305,19 @@ static bool CollectCrashDumpWithCreateDump(Process process, string crashDumpPath } else { - createdump.Kill(true); + // Workaround for https://github.com/dotnet/runtime/issues/93321 + for (int i = 0; i < 5; i++) + { + try + { + createdump.Kill(entireProcessTree: true); + break; + } + catch (Exception e) + { + Console.WriteLine($"Process.Kill(entireProcessTree: true) failed with {e}. Retrying."); + } + } } return fSuccess && createdump.ExitCode == 0; diff --git a/src/tests/Common/Directory.Build.targets b/src/tests/Common/Directory.Build.targets index 2d447a14a1b6d..ae168e70984cc 100644 --- a/src/tests/Common/Directory.Build.targets +++ b/src/tests/Common/Directory.Build.targets @@ -3,7 +3,7 @@ Common files don't take part in the root tests\src\Directory.Build.targets This file prevents them from including it as it gets included in its place If they ever need to take part, we can conditionally include them as documented - here https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets + here https://learn.microsoft.com/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets --> <Import Project="$(MSBuildThisFileDirectory)/disableversioncheck.targets" Condition="'$(DisableVersionCheckImported)' != 'true'" /> diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 73bdcbbec0024..e1a3d2294618d 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -47,6 +47,17 @@ } }"; +const string SimpleVecOpTest_ValidationLogicForNarrowing = @"for (var i = 0; i < Op1ElementCount; i++) + { + if ({ValidateIterResult}) + { + succeeded = false; + break; + } + }"; + +const string SimpleVecOpTest_VectorValidationLogic = @"succeeded = !({ValidateVectorResult});"; + const string SimpleTernVecOpTest_ValidationLogic = @"for (var i = 0; i < RetElementCount; i++) { if ({ValidateIterResult}) @@ -67,6 +78,22 @@ } }"; +const string SimpleVecOpTest_VectorValidationLogicForCndSel = @" + { + {Op1BaseType}[] vectorResult = {GetVectorResult}; + {Op1BaseType}[] maskedVectorResult = new {RetBaseType}[vectorResult.Length]; + + for (var i = 0; i < vectorResult.Length; i++) + { + maskedVectorResult[i] = (mask[i] != 0) ? vectorResult[i] : falseVal[i]; + } + + if (!result.SequenceEqual(maskedVectorResult)) + { + succeeded = false; + } + }"; + const string SimpleTernVecOpTest_ValidationLogicForCndSel = @"for (var i = 0; i < RetElementCount; i++) { {Op1BaseType} iterResult = (mask[i] != 0) ? {GetIterResult} : falseVal[i]; @@ -141,14 +168,20 @@ ("_TernaryOpTestTemplate.template", "SecureHashTernOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "SecureHash", ["TemplateValidationLogic"] = SecureHashOpTest_ValidationLogic }), ("_SveUnaryOpTestTemplate.template", "SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), ("_SveBinaryOpTestTemplate.template", "SveVecBinOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), + ("_SveBinaryOpTestTemplate.template", "SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_VectorValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_VectorValidationLogicForCndSel }), ("_SveBinaryOpTestTemplate.template", "SveVecBinOpConvertTest.template",new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel }), + ("_SveBinaryMaskOpTestTemplate.template", "SveMaskVecBinOpConvertTest.template",new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), ("_SveImmBinaryOpTestTemplate.template", "SveVecImmBinOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel }), ("_SveTernOpTestTemplate.template", "SveVecTernOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel }), ("_SveTernOpFirstArgTestTemplate.template", "SveVecTernOpFirstArgTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel }), ("_SveImmTernOpTestTemplate.template", "SveVecImmTernOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel }), ("_SveImmTernOpFirstArgTestTemplate.template", "SveVecImmTernOpFirstArgTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel }), + ("_SveImm2UnaryOpTestTemplate.template", "SveVecImm2UnOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), ("_SveMinimalUnaryOpTestTemplate.template", "SveVecReduceUnOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecReduceOpTest_ValidationLogic }), - ("_SveMasklessUnaryOpTestTemplate.template","SveMasklessSimpleVecOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_SveMasklessUnaryOpTestTemplate.template", "SveMasklessSimpleVecOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_SveMasklessBinaryOpTestTemplate.template", "SveMasklessVecBinOpTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_SveStoreTemplate.template", "SveStoreTest.template", new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), + ("_SveStoreTemplate.template", "SveStoreNonTemporalTest.template",new Dictionary<string, string> { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), }; (string templateFileName, Dictionary<string, string> templateData)[] AdvSimdInputs = new [] @@ -3004,6 +3037,30 @@ ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_BooleanNot_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "BooleanNot", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.BooleanNot(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.BooleanNot(leftOp[i])"}), ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_BooleanNot_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "BooleanNot", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.BooleanNot(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.BooleanNot(leftOp[i])"}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinOpVecTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compact_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute16BitAddresses_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(left[i] + ((uint)right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + ((uint)right[i] * 2))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute16BitAddresses_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(left[i] + (right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 2))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute16BitAddresses_ulong_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(left[i] + ((ulong)right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + ((ulong)right[i] * 2))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute16BitAddresses_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(left[i] + (right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 2))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute32BitAddresses_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute32BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(left[i] + ((uint)right[i] * 4)) != result[i]", ["GetIterResult"] = "(left[i] + ((uint)right[i] * 4))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute32BitAddresses_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute32BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(left[i] + (right[i] * 4)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 4))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute32BitAddresses_ulong_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute32BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(left[i] + ((ulong)right[i] * 4)) != result[i]", ["GetIterResult"] = "(left[i] + ((ulong)right[i] * 4))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute32BitAddresses_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute32BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(left[i] + (right[i] * 4)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 4))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute64BitAddresses_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute64BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(left[i] + ((uint)right[i] * 8)) != result[i]", ["GetIterResult"] = "(left[i] + ((uint)right[i] * 8))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute64BitAddresses_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute64BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(left[i] + (right[i] * 8)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 8))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute64BitAddresses_ulong_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute64BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(left[i] + ((ulong)right[i] * 8)) != result[i]", ["GetIterResult"] = "(left[i] + ((ulong)right[i] * 8))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute64BitAddresses_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute64BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(left[i] + (right[i] * 8)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 8))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute8BitAddresses_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute8BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(left[i] + ((uint)right[i] * 1)) != result[i]", ["GetIterResult"] = "(left[i] + ((uint)right[i] * 1))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute8BitAddresses_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute8BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(left[i] + (right[i] * 1)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 1))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute8BitAddresses_ulong_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute8BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(left[i] + ((ulong)right[i] * 1)) != result[i]", ["GetIterResult"] = "(left[i] + ((ulong)right[i] * 1))"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Compute8BitAddresses_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute8BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(left[i] + (right[i] * 1)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 1))"}), + ("SveConditionalSelect.template", new Dictionary<string, string> { ["TestName"] = "Sve_ConditionalSelect_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), ("SveConditionalSelect.template", new Dictionary<string, string> { ["TestName"] = "Sve_ConditionalSelect_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), ("SveConditionalSelect.template", new Dictionary<string, string> { ["TestName"] = "Sve_ConditionalSelect_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalSelect", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(firstOp[i] != 0 ? (result[i] != secondOp[i]) : (result[i] != thirdOp[i]))",}), @@ -3105,6 +3162,17 @@ ("SveCreateTrueMaskTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_CreateTrueMaskUInt32", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CreateTrueMaskUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1Type"] = "SveMaskPattern"}), ("SveCreateTrueMaskTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_CreateTrueMaskUInt64", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CreateTrueMaskUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1Type"] = "SveMaskPattern"}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskByte()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskSByte()", ["NextValueOp2"] = "Helpers.getMaskSByte()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt16()", ["NextValueOp2"] = "Helpers.getMaskInt16()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "Helpers.getMaskInt32()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "Helpers.getMaskInt64()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskSingle()", ["NextValueOp2"] = "Helpers.getMaskSingle()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskDouble()", ["NextValueOp2"] = "Helpers.getMaskDouble()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt16()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveVecReduceToScalarBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_GetActiveElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "GetActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "if (Helpers.MaskBothSet(left, right) != result) succeeded = false;",}), + ("SveMasklessSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LeadingSignCount_byte_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LeadingSignCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.CountLeadingSignBits(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.CountLeadingSignBits(leftOp[i])"}), ("SveMasklessSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LeadingSignCount_ushort_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LeadingSignCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.CountLeadingSignBits(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.CountLeadingSignBits(leftOp[i])"}), ("SveMasklessSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LeadingSignCount_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LeadingSignCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.CountLeadingSignBits(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.CountLeadingSignBits(leftOp[i])"}), @@ -3118,41 +3186,98 @@ ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LeadingZeroCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LeadingZeroCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.CountLeadingZeroBits(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.CountLeadingZeroBits(leftOp[i])"}), ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LeadingZeroCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LeadingZeroCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.CountLeadingZeroBits(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.CountLeadingZeroBits(leftOp[i])"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_float", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_double", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_sbyte", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_short", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_int", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_long", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_byte", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_ushort", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_uint", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVector_ulong", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt16SignExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt16SignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt16SignExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt16SignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt32SignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt32SignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorInt32SignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt32SignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToUInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorSByteSignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToUInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16ZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16ZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16ZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16ZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt32ZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32ZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), - ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt32ZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32ZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_float", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_double", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_sbyte", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_short", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_int", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_long", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_byte", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_ushort", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_uint", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVector_ulong", ["Isa"] = "Sve", ["Method"] = "LoadVector", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt16NonFaultingSignExtendToInt32_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt16NonFaultingSignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt16NonFaultingSignExtendToInt64_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt16NonFaultingSignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt16NonFaultingSignExtendToUInt32_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt16NonFaultingSignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "((uint)firstOp[i]) != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt16NonFaultingSignExtendToUInt64_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt16NonFaultingSignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "((ulong)firstOp[i]) != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt32NonFaultingSignExtendToInt64_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt32NonFaultingSignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorInt32NonFaultingSignExtendToUInt64_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorInt32NonFaultingSignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "((ulong)firstOp[i]) != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToInt16_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToInt32_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToInt64_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToUInt16_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "((ushort)firstOp[i]) != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToUInt32_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "((uint)firstOp[i]) != result[i]",}), + ("SveLoadNonFaultingUnOpTest.template",new Dictionary<string, string> {["TestName"] = "Sve_LoadVectorSByteNonFaultingSignExtendToUInt64_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "LoadVectorSByteNonFaultingSignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "((ulong)firstOp[i]) != result[i]",}), + + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt16SignExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt16SignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt16SignExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt16SignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt16SignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt32SignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt32SignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorInt32SignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorInt32SignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToUInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorSByteSignExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorSByteSignExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(ulong)firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToUInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorByteZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt16ZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt16ZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt16ZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt16ZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16ZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt32ZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32ZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadMaskedUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_LoadVectorUInt32ZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32ZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToUInt16", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToUInt16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorByteNonFaultingZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorByteNonFaultingZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16NonFaultingZeroExtendToInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16NonFaultingZeroExtendToInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16NonFaultingZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16NonFaultingZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16NonFaultingZeroExtendToUInt32", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16NonFaultingZeroExtendToUInt32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt16NonFaultingZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt16NonFaultingZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt32NonFaultingZeroExtendToInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32NonFaultingZeroExtendToInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + ("SveLoadNonFaultingUnOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveLoadVectorUInt32NonFaultingZeroExtendToUInt64", ["Isa"] = "Sve", ["Method"] = "LoadVectorUInt32NonFaultingZeroExtendToUInt64", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "firstOp[i] != result[i]"}), + + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad2xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load2xVectorAndUnzip_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load2xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 2] || result2[i] != input[i * 2 + 1]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad3xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load3xVectorAndUnzip_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load3xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 3] || result2[i] != input[i * 3 + 1] || result3[i] != input[i * 3 + 2]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), + ("SveLoad4xVectorAndUnzipTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Load4xVectorAndUnzip_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Load4xVectorAndUnzip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result1[i] != input[i * 4] || result2[i] != input[i * 4 + 1] || result3[i] != input[i * 4 + 2] || result4[i] != input[i * 4 + 3]"}), ("SveVecBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Max_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Max", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.Max(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.Max(left[i], right[i])"}), ("SveVecBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_Max_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Max", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.Max(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.Max(left[i], right[i])"}), @@ -3281,6 +3406,108 @@ ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_PopCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "PopCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.BitCount(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.BitCount(leftOp[i])"}), ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_PopCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "PopCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.BitCount(firstOp[i]) != result[i]", ["GetIterResult"] = "Helpers.BitCount(leftOp[i])"}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.LargestPowerOf2", ["InvalidImm"] = "(Byte)0", ["InvalidImm2"] = "(SveMaskPattern)35", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)12", ["Imm2"] = "SveMaskPattern.VectorCount1", ["InvalidImm"] = "(Byte)19", ["InvalidImm2"] = "(SveMaskPattern)37", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)5", ["Imm2"] = "SveMaskPattern.VectorCount2", ["InvalidImm"] = "(Byte)25", ["InvalidImm2"] = "(SveMaskPattern)46", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)7", ["Imm2"] = "SveMaskPattern.VectorCount3", ["InvalidImm"] = "(Byte)255", ["InvalidImm2"] = "(SveMaskPattern)50", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_vector_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["Imm"] = "(Byte)10", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)65", ["InvalidImm2"] = "(SveMaskPattern)90", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((short)firstOp[i], (short)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy16BitElementCount_vector_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy16BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["Imm"] = "(Byte)1", ["Imm2"] = "SveMaskPattern.VectorCount5", ["InvalidImm"] = "(Byte)72", ["InvalidImm2"] = "(SveMaskPattern)35", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((ushort)firstOp[i], (ushort)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)1", ["Imm2"] = "SveMaskPattern.VectorCount6", ["InvalidImm"] = "(Byte)17", ["InvalidImm2"] = "(SveMaskPattern)32", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount7", ["InvalidImm"] = "(Byte)19", ["InvalidImm2"] = "(SveMaskPattern)33", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)3", ["Imm2"] = "SveMaskPattern.VectorCount8", ["InvalidImm"] = "(Byte)25", ["InvalidImm2"] = "(SveMaskPattern)34", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)4", ["Imm2"] = "SveMaskPattern.VectorCount32", ["InvalidImm"] = "(Byte)26", ["InvalidImm2"] = "(SveMaskPattern)35", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_vector_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)5", ["Imm2"] = "SveMaskPattern.VectorCount64", ["InvalidImm"] = "(Byte)27", ["InvalidImm2"] = "(SveMaskPattern)36", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((int)firstOp[i], (int)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy32BitElementCount_vector_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy32BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)6", ["Imm2"] = "SveMaskPattern.VectorCount128", ["InvalidImm"] = "(Byte)18", ["InvalidImm2"] = "(SveMaskPattern)37", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((uint)firstOp[i], (uint)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)7", ["Imm2"] = "SveMaskPattern.VectorCount256", ["InvalidImm"] = "(Byte)34", ["InvalidImm2"] = "(SveMaskPattern)135", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)8", ["Imm2"] = "SveMaskPattern.LargestMultipleOf4", ["InvalidImm"] = "(Byte)35", ["InvalidImm2"] = "(SveMaskPattern)125", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)9", ["Imm2"] = "SveMaskPattern.LargestMultipleOf3", ["InvalidImm"] = "(Byte)36", ["InvalidImm2"] = "(SveMaskPattern)115", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)10", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)37", ["InvalidImm2"] = "(SveMaskPattern)145", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_vector_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)11", ["Imm2"] = "SveMaskPattern.VectorCount7", ["InvalidImm"] = "(Byte)38", ["InvalidImm2"] = "(SveMaskPattern)155", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((long)firstOp[i], (long)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy64BitElementCount_vector_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy64BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)12", ["Imm2"] = "SveMaskPattern.LargestPowerOf2", ["InvalidImm"] = "(Byte)39", ["InvalidImm2"] = "(SveMaskPattern)165", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate((ulong)firstOp[i], (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy8BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy8BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)13", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)89", ["InvalidImm2"] = "(SveMaskPattern)206", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy8BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy8BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)14", ["Imm2"] = "SveMaskPattern.LargestPowerOf2", ["InvalidImm"] = "(Byte)0", ["InvalidImm2"] = "(SveMaskPattern)207", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy8BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy8BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)15", ["Imm2"] = "SveMaskPattern.VectorCount6", ["InvalidImm"] = "(Byte)91", ["InvalidImm2"] = "(SveMaskPattern)208", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementBy8BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementBy8BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)16", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)92", ["InvalidImm2"] = "(SveMaskPattern)209", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.SubtractSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_int_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_int_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_int_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_int_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_long_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_long_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_long_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_long_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_uint_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_uint_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_uint_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_uint_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_ulong_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_ulong_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_ulong_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.SubtractSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "Helpers.getMaskInt16()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (short)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskInt32()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (int)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskInt64()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (long)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (ushort)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (uint)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingDecrementByActiveElementCount_vector_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingDecrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateIterResult"] = "result[i] != Helpers.SubtractSaturate(left[i], (ulong)(Helpers.NumberOfActiveElementsInMask(right)))", ["GetIterResult"] = "Helpers.SubtractSaturate(left[i], (ulong)(Helpers.NumberOfActiveElementsInMask(right)))"}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)15", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)98", ["InvalidImm2"] = "(SveMaskPattern)241", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)14", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)99", ["InvalidImm2"] = "(SveMaskPattern)242", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)13", ["Imm2"] = "SveMaskPattern.VectorCount256", ["InvalidImm"] = "(Byte)101", ["InvalidImm2"] = "(SveMaskPattern)243", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)12", ["Imm2"] = "SveMaskPattern.VectorCount32", ["InvalidImm"] = "(Byte)118", ["InvalidImm2"] = "(SveMaskPattern)50", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_vector_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["Imm"] = "(Byte)11", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)118", ["InvalidImm2"] = "(SveMaskPattern)60", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((short)firstOp[i], (short)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy16BitElementCount_vector_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy16BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["Imm"] = "(Byte)10", ["Imm2"] = "SveMaskPattern.VectorCount64", ["InvalidImm"] = "(Byte)123", ["InvalidImm2"] = "(SveMaskPattern)70", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((ushort)firstOp[i], (ushort)(imm1 * Helpers.NumberOfElementsInVectorInt16(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)9", ["Imm2"] = "SveMaskPattern.VectorCount5", ["InvalidImm"] = "(Byte)201", ["InvalidImm2"] = "(SveMaskPattern)80", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)8", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)202", ["InvalidImm2"] = "(SveMaskPattern)12", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)7", ["Imm2"] = "SveMaskPattern.LargestPowerOf2", ["InvalidImm"] = "(Byte)207", ["InvalidImm2"] = "(SveMaskPattern)13", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)6", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)220", ["InvalidImm2"] = "(SveMaskPattern)99", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_vector_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)5", ["Imm2"] = "SveMaskPattern.LargestMultipleOf4", ["InvalidImm"] = "(Byte)221", ["InvalidImm2"] = "(SveMaskPattern)76", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((int)firstOp[i], (int)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy32BitElementCount_vector_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy32BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)4", ["Imm2"] = "SveMaskPattern.LargestMultipleOf3", ["InvalidImm"] = "(Byte)240", ["InvalidImm2"] = "(SveMaskPattern)32", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((uint)firstOp[i], (uint)(imm1 * Helpers.NumberOfElementsInVectorInt32(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)3", ["Imm2"] = "SveMaskPattern.VectorCount64", ["InvalidImm"] = "(Byte)241", ["InvalidImm2"] = "(SveMaskPattern)105", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)255", ["InvalidImm2"] = "(SveMaskPattern)108", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)1", ["Imm2"] = "SveMaskPattern.VectorCount6", ["InvalidImm"] = "(Byte)243", ["InvalidImm2"] = "(SveMaskPattern)109", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.LargestMultipleOf3", ["InvalidImm"] = "(Byte)19", ["InvalidImm2"] = "(SveMaskPattern)101", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2))));",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_vector_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.LargestPowerOf2", ["InvalidImm"] = "(Byte)56", ["InvalidImm2"] = "(SveMaskPattern)109", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((long)firstOp[i], (long)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2)))",}), + ("SveVecImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy64BitElementCount_vector_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy64BitElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount8", ["InvalidImm"] = "(Byte)75", ["InvalidImm2"] = "(SveMaskPattern)154", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate((ulong)firstOp[i], (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt64(imm2)))",}), + + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy8BitElementCount_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy8BitElementCount", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount8", ["InvalidImm"] = "(Byte)100", ["InvalidImm2"] = "(SveMaskPattern)235", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((int)data, (int)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy8BitElementCount_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy8BitElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount4", ["InvalidImm"] = "(Byte)99", ["InvalidImm2"] = "(SveMaskPattern)123", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((long)data, (long)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy8BitElementCount_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy8BitElementCount", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.VectorCount5", ["InvalidImm"] = "(Byte)98", ["InvalidImm2"] = "(SveMaskPattern)232", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((uint)data, (uint)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + ("ScalarImm2UnOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementBy8BitElementCount_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementBy8BitElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Byte", ["Op3BaseType"] = "SveMaskPattern", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["Imm"] = "(Byte)2", ["Imm2"] = "SveMaskPattern.All", ["InvalidImm"] = "(Byte)97", ["InvalidImm2"] = "(SveMaskPattern)234", ["ValidateResult"] = "isUnexpectedResult = (result != Helpers.AddSaturate((ulong)data, (ulong)(imm1 * Helpers.NumberOfElementsInVectorInt8(imm2))));",}), + + + + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_int_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_int_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_int_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_int_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (int)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_long_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_long_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_long_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_long_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (long)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_uint_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_uint_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_uint_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_uint_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (uint)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_ulong_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskByte()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_ulong_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveSaturatingByActiveElementCount.template",new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_ulong_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateResult"] = "succeeded = (result == Helpers.AddSaturate(left, (ulong)Helpers.NumberOfActiveElementsInMask(right)));",}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "Helpers.getMaskInt16()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (short)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "Helpers.getMaskInt32()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (int)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "Helpers.getMaskInt64()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (long)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "Helpers.getMaskUInt16()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (ushort)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "Helpers.getMaskUInt32()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (uint)Helpers.NumberOfActiveElementsInMask(right))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], Helpers.NumberOfActiveElementsInMask(right))"}), + ("SveMasklessVecBinOpTest.template", new Dictionary<string, string> {["TestName"] = "Sve_SaturatingIncrementByActiveElementCount_vector_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SaturatingIncrementByActiveElementCount", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "Helpers.getMaskUInt64()", ["ValidateIterResult"] = "result[i] != Helpers.AddSaturate(left[i], (ulong)(Helpers.NumberOfActiveElementsInMask(right)))", ["GetIterResult"] = "Helpers.AddSaturate(left[i], (ulong)(Helpers.NumberOfActiveElementsInMask(right)))"}), + + + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_SignExtend16_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SignExtend16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result[i] != Helpers.SignExtend<int>(firstOp[i], 16, false)", ["GetIterResult"] = "Helpers.SignExtend<int>(leftOp[i], 16, false)"}), ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_SignExtend16_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SignExtend16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != Helpers.SignExtend<long>(firstOp[i], 16, false)", ["GetIterResult"] = "Helpers.SignExtend<long>(leftOp[i], 16, false)"}), ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "Sve_SignExtend32_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "SignExtend32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != Helpers.SignExtend<long>(firstOp[i], 32, false)", ["GetIterResult"] = "Helpers.SignExtend<long>(leftOp[i], 32, false)"}), @@ -3389,16 +3616,16 @@ ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveZipLow_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZipLow", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "result[i] != left[index] || result[i + 1] != right[index]"}), ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveZipLow_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZipLow", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "result[i] != left[index] || result[i + 1] != right[index]"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), - ("SveStoreAndZipTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZip_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), ("SveStoreAndZipTestx2.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZipx2_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateEntry"] = "(maskArray[i] == 0 && (result[index] != 0 || result[index + 1] != 0)) || (maskArray[i] == 1 && (result[index] != first[i] || result[index + 1] != second[i]))"}), ("SveStoreAndZipTestx2.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZipx2_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateEntry"] = "(maskArray[i] == 0 && (result[index] != 0 || result[index + 1] != 0)) || (maskArray[i] == 1 && (result[index] != first[i] || result[index + 1] != second[i]))"}), ("SveStoreAndZipTestx2.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZipx2_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateEntry"] = "(maskArray[i] == 0 && (result[index] != 0 || result[index + 1] != 0)) || (maskArray[i] == 1 && (result[index] != first[i] || result[index + 1] != second[i]))"}), @@ -3430,18 +3657,73 @@ ("SveStoreAndZipTestx4.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZipx4_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && (result[index] != 0 || result[index + 1] != 0 || result[index + 2] != 0 || result[index + 3] != 0)) || (maskArray[i] == 1 && (result[index] != first[i] || result[index + 1] != second[i] || result[index + 2] != third[i] || result[index + 3] != fourth[i]))"}), ("SveStoreAndZipTestx4.template", new Dictionary<string, string> { ["TestName"] = "SveStoreAndZipx4_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreAndZip", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && (result[index] != 0 || result[index + 1] != 0 || result[index + 2] != 0 || result[index + 3] != 0)) || (maskArray[i] == 1 && (result[index] != first[i] || result[index + 1] != second[i] || result[index + 2] != third[i] || result[index + 3] != fourth[i]))"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_short_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_int_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_int_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int16)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int16)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int32)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ushort_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_uint_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_uint_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt16)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt16)first[i])"}), - ("SveStoreNarrowingTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt32)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_short_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_int_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_int_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int16)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (SByte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int16)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_long_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Int32)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ushort_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_uint_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_uint_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt16)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (Byte)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt16)first[i])"}), + ("SveStoreTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNarrow_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNarrowing", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != (UInt32)first[i])"}), + + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + ("SveStoreNonTemporalTest.template", new Dictionary<string, string> { ["TestName"] = "SveStoreNonTemporal_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "StoreNonTemporal", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(maskArray[i] == 0 && result[i] != 0) || (maskArray[i] == 1 && result[i] != first[i])"}), + + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result[i] != firstOp[RetElementCount - i - 1]", ["GetIterResult"] = "leftOp[RetElementCount - i - 1]"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement8_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement8", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement8(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement8(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement16_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement16(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement16(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement16_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement16(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement16(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement16_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement16(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement16(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement16_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement16", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement16(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement16(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement32_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement32(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement32(leftOp[i])"}), + ("SveSimpleVecOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveReverseElement32_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ReverseElement32", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "result[i] != Helpers.ReverseElement32(firstOp[i])", ["GetIterResult"] = "Helpers.ReverseElement32(leftOp[i])"}), + + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeEven_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeEven", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "result[i] != left[index * 2] || result[i + 1] != right[index * 2]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), + ("SveVecPairBinOpTest.template", new Dictionary<string, string> { ["TestName"] = "SveTransposeOdd_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "TransposeOdd", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateEntry"] = "result[i] != left[index * 2 + 1] || result[i + 1] != right[index * 2 + 1]"}), }; @@ -3481,7 +3763,6 @@ void ProcessInputs(string groupName, (string templateFileName, Dictionary<string } } } - void ProcessInput(StreamWriter testListFile, string groupName, (string templateFileName, Dictionary<string, string> templateData) input) { var testName = input.templateData["TestName"]; diff --git a/src/tests/Common/Platform/platformdefines.h b/src/tests/Common/Platform/platformdefines.h index a6eb9c6eae961..2c1f13fbaa78b 100644 --- a/src/tests/Common/Platform/platformdefines.h +++ b/src/tests/Common/Platform/platformdefines.h @@ -191,7 +191,7 @@ inline void *CoreClrBStrAlloc(size_t cb) { // A null is automatically applied in the SysAllocStringByteLen API. // Remove a single OLECHAR for the implied null. - // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/oleauto/nf-oleauto-sysallocstringbytelen + // https://learn.microsoft.com/previous-versions/windows/desktop/api/oleauto/nf-oleauto-sysallocstringbytelen if (cb >= sizeof(OLECHAR)) cb -= sizeof(OLECHAR); diff --git a/src/tests/CoreMangLib/system/delegate/delegate/delegategethashcode1.cs b/src/tests/CoreMangLib/system/delegate/delegate/delegategethashcode1.cs index 7eeb9e55b3a5b..4b09ef2d4cdaf 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/delegategethashcode1.cs +++ b/src/tests/CoreMangLib/system/delegate/delegate/delegategethashcode1.cs @@ -43,8 +43,6 @@ public bool RunTests() retVal = PosTest1() && retVal; retVal = PosTest2() && retVal; retVal = PosTest3() && retVal; - retVal = PosTest4() && retVal; - retVal = PosTest6() && retVal; retVal = PosTest7() && retVal; retVal = PosTest8() && retVal; return retVal; @@ -150,71 +148,6 @@ public bool PosTest3() // Returns true if the expected result is right // Returns false if the expected result is wrong // the same delegate object is booldelegate - public bool PosTest4() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest4: Use the same type's different static method to create two delegate which delegate object is the same,their hashcode is equal "); - - try - { - DelegateGetHashCode delctor = new DelegateGetHashCode(); - booldelegate workDelegate1= new booldelegate(TestClass.Working_Bool); - booldelegate workDelegate = new booldelegate(TestClass.Completed_Bool); - if (workDelegate.GetHashCode() != workDelegate1.GetHashCode()) - { - TestLibrary.TestFramework.LogError("007", "HashCode is not excepted "); - retVal = false; - } - - workDelegate(); - workDelegate1(); - - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("008", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - // Returns true if the expected result is right - // Returns false if the expected result is wrong - // the same delegate object is booldelegate - public bool PosTest6() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest6: Use the different type's same static method to create two delegate ,which delegate object is the same,their hashcode is equal"); - - try - { - DelegateGetHashCode delctor = new DelegateGetHashCode(); - booldelegate workDelegate = new booldelegate(TestClass.Completed_Bool); - booldelegate workDelegate1 = new booldelegate(TestClass1.Completed_Bool); - - if (workDelegate.GetHashCode()!=workDelegate1.GetHashCode()) - { - TestLibrary.TestFramework.LogError("011", "HashCode is not excepted"); - retVal = false; - } - - workDelegate(); - workDelegate1(); - - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("012", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - // Returns true if the expected result is right - // Returns false if the expected result is wrong - // the same delegate object is booldelegate public bool PosTest7() { bool retVal = true; diff --git a/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs b/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs index 2ae25d0391460..b429668d461ac 100644 --- a/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs +++ b/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.cs @@ -10,7 +10,7 @@ public static class DynamicMethodJumpStubTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] + [Fact] public static void TestEntryPoint() { DynamicMethodJumpStubTest(); diff --git a/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.csproj b/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.csproj index 434b851572da8..f60da6be697e8 100644 --- a/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.csproj +++ b/src/tests/CoreMangLib/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses Reflection.Emit --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="$(MSBuildProjectName).cs" /> diff --git a/src/tests/CoreMangLib/system/span/SlowTailCallArgs.cs b/src/tests/CoreMangLib/system/span/SlowTailCallArgs.cs index 931df4c7bd835..f8e44485fc9f1 100644 --- a/src/tests/CoreMangLib/system/span/SlowTailCallArgs.cs +++ b/src/tests/CoreMangLib/system/span/SlowTailCallArgs.cs @@ -9,7 +9,7 @@ public static class Program { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] + [Fact] public static int TestEntryPoint() { bool allPassed = true; diff --git a/src/tests/CoreMangLib/system/span/SlowTailCallArgs.csproj b/src/tests/CoreMangLib/system/span/SlowTailCallArgs.csproj index 795851c18ed66..7e9b7b8fc1dcf 100644 --- a/src/tests/CoreMangLib/system/span/SlowTailCallArgs.csproj +++ b/src/tests/CoreMangLib/system/span/SlowTailCallArgs.csproj @@ -5,6 +5,8 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> <GCStressIncompatible>true</GCStressIncompatible> + <!-- Test uses Reflection.Emit --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="SlowTailCallArgs.cs" /> diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 0ffb806ca2cd2..29668c966d6f3 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -4,7 +4,7 @@ Common files don't take part in the root src\tests\Directory.Build.targets This file prevents them from including it as it gets included in its place If they ever need to take part, we can conditionally include them as documented - here https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets + here https://learn.microsoft.com/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets --> <Import Project="$(MSBuildThisFileDirectory)/Common/disableversioncheck.targets" Condition="'$(DisableVersionCheckImported)' != 'true'" /> @@ -123,6 +123,7 @@ <_WillCLRTestProjectBuild Condition="'$(NativeAotIncompatible)' == 'true' and '$(TestBuildMode)' == 'nativeaot'">false</_WillCLRTestProjectBuild> <_WillCLRTestProjectBuild Condition="'$(RuntimeFlavor)' == 'mono' And '$(AlwaysUseCrossgen2)' == 'true'">false</_WillCLRTestProjectBuild> <_WillCLRTestProjectBuild Condition="'$(EnableNativeSanitizers)' != '' And '$(AlwaysUseCrossgen2)' == 'true'">false</_WillCLRTestProjectBuild> + <_WillCLRTestProjectBuild Condition="'$(TestBuildMode)' == 'nativeaot' And '$(AlwaysUseCrossgen2)' == 'true'">false</_WillCLRTestProjectBuild> </PropertyGroup> <PropertyGroup> <_CopyNativeProjectBinaries Condition="'$(__CopyNativeTestBinaries)' != '1'">$(__CopyNativeProjectsAfterCombinedTestBuild)</_CopyNativeProjectBinaries> diff --git a/src/tests/GC/LargeMemory/memcheck.cs b/src/tests/GC/LargeMemory/memcheck.cs index 5fbda71539022..9f566301c7fd7 100644 --- a/src/tests/GC/LargeMemory/memcheck.cs +++ b/src/tests/GC/LargeMemory/memcheck.cs @@ -75,7 +75,7 @@ private static uint BytesToMB(ulong bytes) => [DllImport("kernel32.dll", SetLastError = true)] static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); - // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex + // https://learn.microsoft.com/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex [StructLayout(LayoutKind.Sequential)] public struct MEMORYSTATUSEX { diff --git a/src/tests/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/tests/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp index 69d9510225030..bcb57c5be6b2c 100644 --- a/src/tests/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp +++ b/src/tests/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp @@ -17,7 +17,7 @@ namespace API { - // Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ + // Documentation found at https://learn.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/ //64bd43f8-bfee-4ec4-b7eb-2935158dae21 const GUID IID_IReferenceTrackerTarget = { 0x64bd43f8, 0xbfee, 0x4ec4, { 0xb7, 0xeb, 0x29, 0x35, 0x15, 0x8d, 0xae, 0x21} }; class DECLSPEC_UUID("64bd43f8-bfee-4ec4-b7eb-2935158dae21") IReferenceTrackerTarget : public IUnknown diff --git a/src/tests/Interop/ReadMe.md b/src/tests/Interop/ReadMe.md index 7b7917a55a202..c8d88a9616e0a 100644 --- a/src/tests/Interop/ReadMe.md +++ b/src/tests/Interop/ReadMe.md @@ -14,7 +14,7 @@ A common pattern for testing is using xUnit's `Assert` utilities. These utilitie ### Managed -Managed tests should be designed to use the [SDK style project](https://docs.microsoft.com/en-us/dotnet/core/tools/csproj) system provided by [`dotnet-cli`](https://github.com/dotnet/cli). +Managed tests should be designed to use the [SDK style project](https://learn.microsoft.com/dotnet/core/tools/csproj) system provided by [`dotnet-cli`](https://github.com/dotnet/cli). ### Native @@ -34,7 +34,7 @@ Interop testing is divided into several areas. ### P/Invoke -The P/Invoke bucket represents tests that involve a [Platform Invoke](https://docs.microsoft.com/en-us/dotnet/standard/native-interop) scenario. +The P/Invoke bucket represents tests that involve a [Platform Invoke](https://learn.microsoft.com/dotnet/standard/native-interop) scenario. Testing P/Invoke has two aspects: diff --git a/src/tests/Interop/StructPacking/StructPacking.cs b/src/tests/Interop/StructPacking/StructPacking.cs index 0a2c0bd86c04f..529fe2b72d393 100644 --- a/src/tests/Interop/StructPacking/StructPacking.cs +++ b/src/tests/Interop/StructPacking/StructPacking.cs @@ -1326,9 +1326,9 @@ static bool TestVector256() expectedOffsetValue: 8 ); } - else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64 || RuntimeInformation.ProcessArchitecture == Architecture.RiscV64) { - // The Procedure Call Standard for ARM64 defines this type as having 16-byte alignment + // The Procedure Call Standard for ARM64 and RiscV64 defines this type as having 16-byte alignment succeeded &= Test<DefaultLayoutDefaultPacking<Vector256<byte>>>( expectedSize: 48, diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_32BIT_unaligned_1.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_32BIT_unaligned_1.ilproj index 234ed1f8c99c6..b44468b0a77ab 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_32BIT_unaligned_1.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_32BIT_unaligned_1.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_64BIT_unaligned_1.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_64BIT_unaligned_1.ilproj index 4aa377bfa4bd7..96e442be873cd 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_64BIT_unaligned_1.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_64BIT_unaligned_1.ilproj @@ -7,6 +7,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetBits)' != '64'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist64.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_ARM_unaligned_1.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_ARM_unaligned_1.ilproj index 5c540429673da..37191cf14f89d 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_ARM_unaligned_1.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/1/arglist_Target_ARM_unaligned_1.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'arm' and '$(TargetArchitecture)' != 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglistARM.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_32BIT_unaligned_2.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_32BIT_unaligned_2.ilproj index 234ed1f8c99c6..b44468b0a77ab 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_32BIT_unaligned_2.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_32BIT_unaligned_2.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_64BIT_unaligned_2.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_64BIT_unaligned_2.ilproj index 4aa377bfa4bd7..96e442be873cd 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_64BIT_unaligned_2.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_64BIT_unaligned_2.ilproj @@ -7,6 +7,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetBits)' != '64'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist64.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_ARM_unaligned_2.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_ARM_unaligned_2.ilproj index 5c540429673da..37191cf14f89d 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_ARM_unaligned_2.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/2/arglist_Target_ARM_unaligned_2.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'arm' and '$(TargetArchitecture)' != 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglistARM.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_32BIT_unaligned_4.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_32BIT_unaligned_4.ilproj index 234ed1f8c99c6..b44468b0a77ab 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_32BIT_unaligned_4.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_32BIT_unaligned_4.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_64BIT_unaligned_4.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_64BIT_unaligned_4.ilproj index 4aa377bfa4bd7..96e442be873cd 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_64BIT_unaligned_4.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_64BIT_unaligned_4.ilproj @@ -7,6 +7,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetBits)' != '64'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist64.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_ARM_unaligned_4.ilproj b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_ARM_unaligned_4.ilproj index 5c540429673da..37191cf14f89d 100644 --- a/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_ARM_unaligned_4.ilproj +++ b/src/tests/JIT/Directed/PREFIX/unaligned/4/arglist_Target_ARM_unaligned_4.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'arm' and '$(TargetArchitecture)' != 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglistARM.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_32BIT_volatile.ilproj b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_32BIT_volatile.ilproj index 234ed1f8c99c6..b44468b0a77ab 100644 --- a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_32BIT_volatile.ilproj +++ b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_32BIT_volatile.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_64BIT_volatile.ilproj b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_64BIT_volatile.ilproj index 4aa377bfa4bd7..96e442be873cd 100644 --- a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_64BIT_volatile.ilproj +++ b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_64BIT_volatile.ilproj @@ -7,6 +7,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetBits)' != '64'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglist64.il" /> diff --git a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_ARM_volatile.ilproj b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_ARM_volatile.ilproj index 5c540429673da..37191cf14f89d 100644 --- a/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_ARM_volatile.ilproj +++ b/src/tests/JIT/Directed/PREFIX/volatile/1/arglist_Target_ARM_volatile.ilproj @@ -8,6 +8,8 @@ <CLRTestTargetUnsupported Condition="'$(TargetArchitecture)' != 'arm' and '$(TargetArchitecture)' != 'armel'">true</CLRTestTargetUnsupported> <DebugType>PdbOnly</DebugType> <Optimize>True</Optimize> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="arglistARM.il" /> diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index fa4424056b397..05dad9f08aa7b 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -8,6 +8,10 @@ using System; using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.Arm; namespace JIT.HardwareIntrinsics.Arm { @@ -6429,5 +6433,409 @@ public static ulong WhileLessThanOrEqualMask(ulong op1, ulong op2) { return (ulong)((op1 <= op2) ? 1 : 0); } + + public static ulong MaskBothSet(byte[] op1, byte[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(sbyte[] op1, sbyte[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(short[] op1, short[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(ushort[] op1, ushort[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(int[] op1, int[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(uint[] op1, uint[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(long[] op1, long[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(ulong[] op1, ulong[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((op1[i]==1 && op2[i]==1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(float[] op1, float[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((BitConverter.SingleToInt32Bits(op1[i]) == 1 && BitConverter.SingleToInt32Bits(op2[i]) == 1) ? 1 : 0); + } + return acc; + } + + public static ulong MaskBothSet(double[] op1, double[] op2) + { + ulong acc = 0; + for (var i = 0; i < op1.Length; i++) + { + acc += (ulong)((BitConverter.DoubleToInt64Bits(op1[i]) == 1 && BitConverter.DoubleToInt64Bits(op2[i]) == 1) ? 1 : 0); + } + return acc; + } + + public static byte getMaskByte() + { + return (byte)(TestLibrary.Generator.GetByte()%(byte)2); + } + + public static sbyte getMaskSByte() + { + return (sbyte)(TestLibrary.Generator.GetSByte()%(sbyte)2); + } + + public static short getMaskInt16() + { + return (short)(TestLibrary.Generator.GetInt16()%(short)2); + } + + public static ushort getMaskUInt16() + { + return (ushort)(TestLibrary.Generator.GetUInt16()%(ushort)2); + } + + public static int getMaskInt32() + { + return (int)(TestLibrary.Generator.GetInt32()%(int)2); + } + + public static uint getMaskUInt32() + { + return (uint)(TestLibrary.Generator.GetUInt32()%(uint)2); + } + + public static long getMaskInt64() + { + return (long)(TestLibrary.Generator.GetInt64()%(long)2); + } + + public static ulong getMaskUInt64() + { + return (ulong)(TestLibrary.Generator.GetUInt64()%(ulong)2); + } + + public static float getMaskSingle() + { + return (float)(BitConverter.Int32BitsToSingle(TestLibrary.Generator.GetInt32()%(int)2)); + } + + public static float getMaskDouble() + { + return (float)(BitConverter.Int64BitsToDouble(TestLibrary.Generator.GetInt64()%(long)2)); + } + + public static int MaskNumberOfElementsVector(int elems, SveMaskPattern pattern) + { + + switch(pattern) + { + // Returns elems, as this is always a power of 2. + case SveMaskPattern.LargestPowerOf2: + return elems; + + // Returns N if N elements can fit in the vector. Otherwise 0. + case SveMaskPattern.VectorCount1: + return elems >= 1 ? 1 : 0; + case SveMaskPattern.VectorCount2: + return elems >= 2 ? 2 : 0; + case SveMaskPattern.VectorCount3: + return elems >= 3 ? 3 : 0; + case SveMaskPattern.VectorCount4: + return elems >= 4 ? 4 : 0; + case SveMaskPattern.VectorCount5: + return elems >= 5 ? 5 : 0; + case SveMaskPattern.VectorCount6: + return elems >= 6 ? 6 : 0; + case SveMaskPattern.VectorCount7: + return elems >= 7 ? 7 : 0; + case SveMaskPattern.VectorCount8: + return elems >= 8 ? 8 : 0; + case SveMaskPattern.VectorCount16: + return elems >= 16 ? 16 : 0; + case SveMaskPattern.VectorCount32: + return elems >= 32 ? 32 : 0; + case SveMaskPattern.VectorCount64: + return elems >= 64 ? 64 : 0; + case SveMaskPattern.VectorCount128: + return elems >= 128 ? 128 : 0; + case SveMaskPattern.VectorCount256: + return elems >= 256 ? 256 : 0; + + // Number of elems rounded down to nearest multiple of 4 + case SveMaskPattern.LargestMultipleOf4: + return elems - (elems % 4); + + // Number of elems rounded down to nearest multiple of 3 + case SveMaskPattern.LargestMultipleOf3: + return elems - (elems % 3); + + case SveMaskPattern.All: + default: + return elems; + } + } + + public static int NumberOfElementsInVectorInt8(SveMaskPattern pattern) + { + return MaskNumberOfElementsVector(Unsafe.SizeOf<Vector<byte>>() / sizeof(byte), pattern); + } + + public static int NumberOfElementsInVectorInt16(SveMaskPattern pattern) + { + return MaskNumberOfElementsVector(Unsafe.SizeOf<Vector<short>>() / sizeof(short), pattern); + } + + public static int NumberOfElementsInVectorInt32(SveMaskPattern pattern) + { + return MaskNumberOfElementsVector(Unsafe.SizeOf<Vector<int>>() / sizeof(int), pattern); + } + + public static int NumberOfElementsInVectorInt64(SveMaskPattern pattern) + { + return MaskNumberOfElementsVector(Unsafe.SizeOf<Vector<long>>() / sizeof(long), pattern); + } + + public static int NumberOfActiveElementsInMask(sbyte[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(short[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(int[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(long[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(byte[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(ushort[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(uint[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static int NumberOfActiveElementsInMask(ulong[] mask) + { + int acc = 0; + for (var i = 0; i < mask.Length; i++) + { + acc += (mask[i] == 1) ? 1 : 0; + } + return acc; + } + + public static double[] Compact(double[] op1, double[] op2) + { + double[] result = new double[op1.Length]; + Array.Fill<double>(result, 0, 0, op1.Length); + + int i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + + public static int[] Compact(int[] op1, int[] op2) + { + int[] result = new int[op1.Length]; + Array.Fill<int>(result, 0, 0, op1.Length); + + int i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + + public static long[] Compact(long[] op1, long[] op2) + { + long[] result = new long[op1.Length]; + Array.Fill<long>(result, 0, 0, op1.Length); + + long i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + + public static float[] Compact(float[] op1, float[] op2) + { + float[] result = new float[op1.Length]; + Array.Fill<float>(result, 0, 0, op1.Length); + + int i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + + public static uint[] Compact(uint[] op1, uint[] op2) + { + uint[] result = new uint[op1.Length]; + Array.Fill<uint>(result, 0, 0, op1.Length); + + int i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + + public static ulong[] Compact(ulong[] op1, ulong[] op2) + { + ulong[] result = new ulong[op1.Length]; + Array.Fill<ulong>(result, 0, 0, op1.Length); + + ulong i = 0; + for (int j = 0; j < op1.Length; j++) + { + if (op1[j] != 0) + { + result[i++] = op2[j]; + } + } + + return result; + } + } } diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/ScalarImm2UnOpTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/ScalarImm2UnOpTest.template new file mode 100644 index 0000000000000..78afd8c2a52df --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/ScalarImm2UnOpTest.template @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new ScalarImm2UnaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead(); + + // Validates calling via reflection works, using Unsafe.ReadUnaligned + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.ReadUnaligned + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + + // Validates basic functionality fails with an invalid imm1, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm1(); + + // Validates basic functionality fails with an invalid imm2, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm2(); + } + 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 ScalarImm2UnaryOpTest__{TestName} + { + private struct TestStruct + { + public {Op1BaseType} _fld; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + testStruct._fld = {NextValueOp1}; + return testStruct; + } + + public void RunStructFldScenario(ScalarImm2UnaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld, {Imm}, {Imm2}); + testClass.ValidateResult(_fld, {Imm}, {Imm2}, result); + } + } + + private static {Op1BaseType} _data; + + private {Op1BaseType} _fld; + + public ScalarImm2UnaryOpTest__{TestName}() + { + Succeeded = true; + + + _fld = {NextValueOp1}; + _data = {NextValueOp1}; + } + + 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.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data)), {Imm}, {Imm2} + ); + + ValidateResult(_data, {Imm}, {Imm2}, result); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1BaseType}), typeof({Op2BaseType}), typeof({Op3BaseType}) }) + .Invoke(null, new object[] { + Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data)), + {Imm}, + {Imm2}, + }); + + ValidateResult(_data, {Imm}, {Imm2}, ({RetBaseType})result); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var data = Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data)); + var result = {Isa}.{Method}(data, {Imm}, {Imm2}); + + ValidateResult(data, {Imm}, {Imm2}, result); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld, {Imm}, {Imm2}); + ValidateResult(_fld, {Imm}, {Imm2}, result); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld, {Imm}, {Imm2}); + + ValidateResult(test._fld, {Imm}, {Imm2}, result); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunBasicScenario_UnsafeRead_InvalidImm1() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm1)); + + bool succeeded = false; + + try + { + var result = {Isa}.{Method}( + Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data)), {InvalidImm}, {Imm2} + ); + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + } + + public void RunBasicScenario_UnsafeRead_InvalidImm2() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm2)); + + bool succeeded = false; + + try + { + var result = {Isa}.{Method}( + Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data)), {Imm}, {InvalidImm2} + ); + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + } + + 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({Op1BaseType} data, {Op2BaseType} imm1, {Op3BaseType} imm2, {RetBaseType} result, [CallerMemberName] string method = "") + { + var isUnexpectedResult = false; + + {ValidateResult} + + if (isUnexpectedResult) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1BaseType}, {Imm}, {Imm2}): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" data: {data}"); + TestLibrary.TestFramework.LogInformation($" result: {result}"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad2xVectorAndUnzipTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad2xVectorAndUnzipTest.template new file mode 100644 index 0000000000000..089e06915159f --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad2xVectorAndUnzipTest.template @@ -0,0 +1,387 @@ +// 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 src\tests\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ +using System; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TestName}Test(); + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + test.RunBasicScenario_FalseMask(); + test.RunBasicScenario_NonFaulting(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // 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 {TestName}Test + { + private struct DataTable + { + private byte[] inArray; + private byte[] outArray1; + private byte[] outArray2; + + private GCHandle inHandle; + private GCHandle outHandle1; + private GCHandle outHandle2; + + private ulong alignment; + + public DataTable({Op1BaseType}[] outArray1, {Op1BaseType}[] outArray2, {Op1BaseType}[] inArray, int alignment) + { + int sizeOfInArray = inArray.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray1 = outArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray2 = outArray2.Length * Unsafe.SizeOf<{Op1BaseType}>(); + if ((alignment != 64 && alignment != 8) || (alignment * 2) < sizeOfInArray || (alignment * 2) < sizeOfOutArray1 || (alignment * 2) < sizeOfOutArray2) + { + throw new ArgumentException("Invalid value of alignment"); + } + + this.inArray = new byte[alignment * 2 * 2]; + this.outArray1 = new byte[alignment * 2]; + this.outArray2 = new byte[alignment * 2]; + + this.inHandle = GCHandle.Alloc(this.inArray, GCHandleType.Pinned); + this.outHandle1 = GCHandle.Alloc(this.outArray1, GCHandleType.Pinned); + this.outHandle2 = GCHandle.Alloc(this.outArray2, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArrayPtr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), (uint)sizeOfInArray); + } + + public void* inArrayPtr => Align((byte*)(inHandle.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray1Ptr => Align((byte*)(outHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray2Ptr => Align((byte*)(outHandle2.AddrOfPinnedObject().ToPointer()), alignment); + + + public void Dispose() + { + inHandle.Free(); + outHandle1.Free(); + outHandle2.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op1VectorType}<{Op1BaseType}> _fld2; + + public static TestStruct Create() + { + return new TestStruct(); + } + + public void RunStructFldScenario({TestName}Test testClass) + { + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)testClass._dataTable.inArrayPtr); + + testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.inArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int OpElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int DestElementCount = OpElementCount * 2; + + private static {Op1BaseType}[] _data = new {Op1BaseType}[DestElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op1VectorType}<{Op1BaseType}> _fld2; + + private DataTable _dataTable; + + public {TestName}Test() + { + Succeeded = true; + for (var i = 0; i < DestElementCount; i++) { _data[i] = {NextValueOp2}; } + _dataTable = new DataTable(new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], _data, LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_FalseMask() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_FalseMask)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_NonFaulting() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_NonFaulting)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadTrueMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + {Op1VectorType}<{Op1BaseType}> loadFalseMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadTrueMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + try + { + result = {Isa}.{Method}(loadFalseMask, default); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.inArrayPtr); + } + catch + { + Succeeded = false; + } + } + + public void RunReflectionScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof(Vector<{Op1BaseType}>), typeof({Op1BaseType}*) }) + .Invoke(null, new object[] { + loadMask, + Pointer.Box(_dataTable.inArrayPtr, typeof({Op1BaseType}*)) + }); + + var output = (({Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>))result; + Unsafe.Write(_dataTable.outArray1Ptr, output.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, output.Item2); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.inArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + ValidateResult(_fld1, _fld2, _dataTable.inArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var test = TestStruct.Create(); + (test._fld1, test._fld2) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + Unsafe.Write(_dataTable.outArray1Ptr, test._fld1); + Unsafe.Write(_dataTable.outArray2Ptr, test._fld2); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.inArrayPtr); + } + + 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 + { + RunClassFldScenario(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult(void* result1, void* result2, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 2); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateResult(inArray, outArray1, outArray2, method); + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + + ValidateResult(inArray, outArray1, outArray2, method); + } + + private void ValidateResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if ({ValidateIterResult}) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateZeroResult(void* result1, void* result2, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 2); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateZeroResult(inArray, outArray1, outArray2, method); + } + + private void ValidateZeroResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + + ValidateZeroResult(inArray, outArray1, outArray2, method); + } + + private void ValidateZeroResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if (result1[i] != 0 || result2[i] != 0) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad3xVectorAndUnzipTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad3xVectorAndUnzipTest.template new file mode 100644 index 0000000000000..ea9e9e25eda88 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad3xVectorAndUnzipTest.template @@ -0,0 +1,411 @@ +// 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 src\tests\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ +using System; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TestName}Test(); + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + test.RunBasicScenario_FalseMask(); + test.RunBasicScenario_NonFaulting(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // 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 {TestName}Test + { + private struct DataTable + { + private byte[] inArray; + private byte[] outArray1; + private byte[] outArray2; + private byte[] outArray3; + + private GCHandle inHandle; + private GCHandle outHandle1; + private GCHandle outHandle2; + private GCHandle outHandle3; + + private ulong alignment; + + public DataTable({Op1BaseType}[] outArray1, {Op1BaseType}[] outArray2, {Op1BaseType}[] outArray3, {Op1BaseType}[] inArray, int alignment) + { + int sizeOfInArray = inArray.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray1 = outArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray2 = outArray2.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray3 = outArray3.Length * Unsafe.SizeOf<{Op1BaseType}>(); + if ((alignment != 64 && alignment != 8) || (alignment * 3) < sizeOfInArray || (alignment * 2) < sizeOfOutArray1 || (alignment * 2) < sizeOfOutArray2 || (alignment * 2) < sizeOfOutArray3) + { + throw new ArgumentException("Invalid value of alignment"); + } + + this.inArray = new byte[alignment * 2 * 3]; + this.outArray1 = new byte[alignment * 2]; + this.outArray2 = new byte[alignment * 2]; + this.outArray3 = new byte[alignment * 2]; + + this.inHandle = GCHandle.Alloc(this.inArray, GCHandleType.Pinned); + this.outHandle1 = GCHandle.Alloc(this.outArray1, GCHandleType.Pinned); + this.outHandle2 = GCHandle.Alloc(this.outArray2, GCHandleType.Pinned); + this.outHandle3 = GCHandle.Alloc(this.outArray3, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArrayPtr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), (uint)sizeOfInArray); + } + + public void* inArrayPtr => Align((byte*)(inHandle.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray1Ptr => Align((byte*)(outHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray2Ptr => Align((byte*)(outHandle2.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray3Ptr => Align((byte*)(outHandle3.AddrOfPinnedObject().ToPointer()), alignment); + + + public void Dispose() + { + inHandle.Free(); + outHandle1.Free(); + outHandle2.Free(); + outHandle3.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op1VectorType}<{Op1BaseType}> _fld2; + public {Op1VectorType}<{Op1BaseType}> _fld3; + + public static TestStruct Create() + { + return new TestStruct(); + } + + public void RunStructFldScenario({TestName}Test testClass) + { + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2, _fld3) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)testClass._dataTable.inArrayPtr); + + testClass.ValidateResult(_fld1, _fld2, _fld3, testClass._dataTable.inArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int OpElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int DestElementCount = OpElementCount * 3; + + private static {Op1BaseType}[] _data = new {Op1BaseType}[DestElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op1VectorType}<{Op1BaseType}> _fld2; + private {Op1VectorType}<{Op1BaseType}> _fld3; + + private DataTable _dataTable; + + public {TestName}Test() + { + Succeeded = true; + for (var i = 0; i < DestElementCount; i++) { _data[i] = {NextValueOp2}; } + _dataTable = new DataTable(new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], _data, LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_FalseMask() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_FalseMask)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_NonFaulting() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_NonFaulting)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadTrueMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + {Op1VectorType}<{Op1BaseType}> loadFalseMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadTrueMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + try + { + result = {Isa}.{Method}(loadFalseMask, default); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.inArrayPtr); + } + catch + { + Succeeded = false; + } + } + + public void RunReflectionScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof(Vector<{Op1BaseType}>), typeof({Op1BaseType}*) }) + .Invoke(null, new object[] { + loadMask, + Pointer.Box(_dataTable.inArrayPtr, typeof({Op1BaseType}*)) + }); + + var output = (({Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>))result; + Unsafe.Write(_dataTable.outArray1Ptr, output.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, output.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, output.Item3); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.inArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2, _fld3) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + ValidateResult(_fld1, _fld2, _fld3, _dataTable.inArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var test = TestStruct.Create(); + (test._fld1, test._fld2, test._fld3) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + Unsafe.Write(_dataTable.outArray1Ptr, test._fld1); + Unsafe.Write(_dataTable.outArray2Ptr, test._fld2); + Unsafe.Write(_dataTable.outArray3Ptr, test._fld3); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.inArrayPtr); + } + + 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 + { + RunClassFldScenario(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult(void* result1, void* result2, void* result3, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 3); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), ref Unsafe.AsRef<byte>(result3), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateResult(inArray, outArray1, outArray2, outArray3, method); + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, {Op1VectorType}<{Op1BaseType}> result3, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 3); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), result3); + + ValidateResult(inArray, outArray1, outArray2, outArray3, method); + } + + private void ValidateResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, {Op1BaseType}[] result3, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if ({ValidateIterResult}) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation($" result3: ({string.Join(", ", result3)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateZeroResult(void* result1, void* result2, void* result3, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 3); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), ref Unsafe.AsRef<byte>(result3), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateZeroResult(inArray, outArray1, outArray2, outArray3, method); + } + + private void ValidateZeroResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, {Op1VectorType}<{Op1BaseType}> result3, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 3); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), result3); + + ValidateZeroResult(inArray, outArray1, outArray2, outArray3, method); + } + + private void ValidateZeroResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, {Op1BaseType}[] result3, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if (result1[i] != 0 || result2[i] != 0 || result3[i] != 0) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation($" result3: ({string.Join(", ", result3)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad4xVectorAndUnzipTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad4xVectorAndUnzipTest.template new file mode 100644 index 0000000000000..ccedc85e7aae1 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoad4xVectorAndUnzipTest.template @@ -0,0 +1,435 @@ +// 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 src\tests\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ +using System; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TestName}Test(); + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario(); + test.RunBasicScenario_FalseMask(); + test.RunBasicScenario_NonFaulting(); + + // Validates calling via reflection works + test.RunReflectionScenario(); + + // 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 {TestName}Test + { + private struct DataTable + { + private byte[] inArray; + private byte[] outArray1; + private byte[] outArray2; + private byte[] outArray3; + private byte[] outArray4; + + private GCHandle inHandle; + private GCHandle outHandle1; + private GCHandle outHandle2; + private GCHandle outHandle3; + private GCHandle outHandle4; + + private ulong alignment; + + public DataTable({Op1BaseType}[] outArray1, {Op1BaseType}[] outArray2, {Op1BaseType}[] outArray3, {Op1BaseType}[] outArray4, {Op1BaseType}[] inArray, int alignment) + { + int sizeOfInArray = inArray.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray1 = outArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray2 = outArray2.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray3 = outArray3.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfOutArray4 = outArray4.Length * Unsafe.SizeOf<{Op1BaseType}>(); + if ((alignment != 64 && alignment != 8) || (alignment * 4) < sizeOfInArray || (alignment * 2) < sizeOfOutArray1 || (alignment * 2) < sizeOfOutArray2 || (alignment * 2) < sizeOfOutArray3 || (alignment * 2) < sizeOfOutArray4) + { + throw new ArgumentException("Invalid value of alignment"); + } + + this.inArray = new byte[alignment * 2 * 4]; + this.outArray1 = new byte[alignment * 2]; + this.outArray2 = new byte[alignment * 2]; + this.outArray3 = new byte[alignment * 2]; + this.outArray4 = new byte[alignment * 2]; + + this.inHandle = GCHandle.Alloc(this.inArray, GCHandleType.Pinned); + this.outHandle1 = GCHandle.Alloc(this.outArray1, GCHandleType.Pinned); + this.outHandle2 = GCHandle.Alloc(this.outArray2, GCHandleType.Pinned); + this.outHandle3 = GCHandle.Alloc(this.outArray3, GCHandleType.Pinned); + this.outHandle4 = GCHandle.Alloc(this.outArray4, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArrayPtr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), (uint)sizeOfInArray); + } + + public void* inArrayPtr => Align((byte*)(inHandle.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray1Ptr => Align((byte*)(outHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray2Ptr => Align((byte*)(outHandle2.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray3Ptr => Align((byte*)(outHandle3.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArray4Ptr => Align((byte*)(outHandle4.AddrOfPinnedObject().ToPointer()), alignment); + + + public void Dispose() + { + inHandle.Free(); + outHandle1.Free(); + outHandle2.Free(); + outHandle3.Free(); + outHandle4.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op1VectorType}<{Op1BaseType}> _fld2; + public {Op1VectorType}<{Op1BaseType}> _fld3; + public {Op1VectorType}<{Op1BaseType}> _fld4; + + public static TestStruct Create() + { + return new TestStruct(); + } + + public void RunStructFldScenario({TestName}Test testClass) + { + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2, _fld3, _fld4) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)testClass._dataTable.inArrayPtr); + + testClass.ValidateResult(_fld1, _fld2, _fld3, _fld4, testClass._dataTable.inArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int OpElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int DestElementCount = OpElementCount * 4; + + private static {Op1BaseType}[] _data = new {Op1BaseType}[DestElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op1VectorType}<{Op1BaseType}> _fld2; + private {Op1VectorType}<{Op1BaseType}> _fld3; + private {Op1VectorType}<{Op1BaseType}> _fld4; + + private DataTable _dataTable; + + public {TestName}Test() + { + Succeeded = true; + for (var i = 0; i < DestElementCount; i++) { _data[i] = {NextValueOp2}; } + _dataTable = new DataTable(new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], new {Op1BaseType}[OpElementCount], _data, LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + Unsafe.Write(_dataTable.outArray4Ptr, result.Item4); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.outArray4Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_FalseMask() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_FalseMask)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + Unsafe.Write(_dataTable.outArray4Ptr, result.Item4); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.outArray4Ptr, _dataTable.inArrayPtr); + } + + public void RunBasicScenario_NonFaulting() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_NonFaulting)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadTrueMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + {Op1VectorType}<{Op1BaseType}> loadFalseMask = Sve.CreateFalseMask{RetBaseType}(); + + var result = {Isa}.{Method}(loadTrueMask, ({Op1BaseType}*)(_dataTable.inArrayPtr)); + + try + { + result = {Isa}.{Method}(loadFalseMask, default); + + Unsafe.Write(_dataTable.outArray1Ptr, result.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, result.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, result.Item3); + Unsafe.Write(_dataTable.outArray4Ptr, result.Item4); + ValidateZeroResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.outArray4Ptr, _dataTable.inArrayPtr); + } + catch + { + Succeeded = false; + } + } + + public void RunReflectionScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof(Vector<{Op1BaseType}>), typeof({Op1BaseType}*) }) + .Invoke(null, new object[] { + loadMask, + Pointer.Box(_dataTable.inArrayPtr, typeof({Op1BaseType}*)) + }); + + var output = (({Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>,{Op1VectorType}<{Op1BaseType}>))result; + Unsafe.Write(_dataTable.outArray1Ptr, output.Item1); + Unsafe.Write(_dataTable.outArray2Ptr, output.Item2); + Unsafe.Write(_dataTable.outArray3Ptr, output.Item3); + Unsafe.Write(_dataTable.outArray4Ptr, output.Item4); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.outArray4Ptr, _dataTable.inArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + (_fld1, _fld2, _fld3, _fld4) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + ValidateResult(_fld1, _fld2, _fld3, _fld4, _dataTable.inArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + //TODO-SVE: Once register allocation exists for predicates, move loadMask into DataTable + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var test = TestStruct.Create(); + (test._fld1, test._fld2, test._fld3, test._fld4) = {Isa}.{Method}(loadMask, ({Op1BaseType}*)_dataTable.inArrayPtr); + + Unsafe.Write(_dataTable.outArray1Ptr, test._fld1); + Unsafe.Write(_dataTable.outArray2Ptr, test._fld2); + Unsafe.Write(_dataTable.outArray3Ptr, test._fld3); + Unsafe.Write(_dataTable.outArray4Ptr, test._fld4); + ValidateResult(_dataTable.outArray1Ptr, _dataTable.outArray2Ptr, _dataTable.outArray3Ptr, _dataTable.outArray4Ptr, _dataTable.inArrayPtr); + } + + 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 + { + RunClassFldScenario(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult(void* result1, void* result2, void* result3, void* result4, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray4 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 4); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), ref Unsafe.AsRef<byte>(result3), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray4[0]), ref Unsafe.AsRef<byte>(result4), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateResult(inArray, outArray1, outArray2, outArray3, outArray4, method); + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, {Op1VectorType}<{Op1BaseType}> result3, {Op1VectorType}<{Op1BaseType}> result4, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray4 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 4); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), result3); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray4[0]), result4); + + ValidateResult(inArray, outArray1, outArray2, outArray3, outArray4, method); + } + + private void ValidateResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, {Op1BaseType}[] result3, {Op1BaseType}[] result4, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if ({ValidateIterResult}) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation($" result3: ({string.Join(", ", result3)})"); + TestLibrary.TestFramework.LogInformation($" result4: ({string.Join(", ", result4)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateZeroResult(void* result1, void* result2, void* result3, void* result4, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray4 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 4); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), ref Unsafe.AsRef<byte>(result1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), ref Unsafe.AsRef<byte>(result2), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), ref Unsafe.AsRef<byte>(result3), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray4[0]), ref Unsafe.AsRef<byte>(result4), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + ValidateZeroResult(inArray, outArray1, outArray2, outArray3, outArray4, method); + } + + private void ValidateZeroResult({Op1VectorType}<{Op1BaseType}> result1, {Op1VectorType}<{Op1BaseType}> result2, {Op1VectorType}<{Op1BaseType}> result3, {Op1VectorType}<{Op1BaseType}> result4, void* input, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[DestElementCount]; + {Op1BaseType}[] outArray1 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray2 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray3 = new {Op1BaseType}[OpElementCount]; + {Op1BaseType}[] outArray4 = new {Op1BaseType}[OpElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(input), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() * 4); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray1[0]), result1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray2[0]), result2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray3[0]), result3); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref outArray4[0]), result4); + + ValidateZeroResult(inArray, outArray1, outArray2, outArray3, outArray4, method); + } + + private void ValidateZeroResult({Op1BaseType}[] input, {Op1BaseType}[] result1, {Op1BaseType}[] result2, {Op1BaseType}[] result3, {Op1BaseType}[] result4, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < OpElementCount; i++) + { + if (result1[i] != 0 || result2[i] != 0 || result3[i] != 0 || result4[i] != 0) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({Op1VectorType}<{Op1BaseType}>): {Method} failed:"); + TestLibrary.TestFramework.LogInformation($" input: ({string.Join(", ", input)})"); + TestLibrary.TestFramework.LogInformation($" result1: ({string.Join(", ", result1)})"); + TestLibrary.TestFramework.LogInformation($" result2: ({string.Join(", ", result2)})"); + TestLibrary.TestFramework.LogInformation($" result3: ({string.Join(", ", result3)})"); + TestLibrary.TestFramework.LogInformation($" result4: ({string.Join(", ", result4)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoadNonFaultingUnOpTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoadNonFaultingUnOpTest.template new file mode 100644 index 0000000000000..a043bd3aede6e --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveLoadNonFaultingUnOpTest.template @@ -0,0 +1,413 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new LoadUnaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works + test.RunBasicScenario_Load(); + + // Validates basic functionality + test.RunBasicScenario_LoadNonFaulting(); + + // Validates calling via reflection works + test.RunReflectionScenario_Load(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + + // Validates using inside ConditionalSelect with value falseValue + // Currently, using this operation in ConditionalSelect() gives incorrect result + // when falseReg == targetReg because this instruction uses Pg/Z to update the targetReg + // instead of Pg/M to merge it. As such, the value of falseReg is lost. Ideally, such + // instructions should be marked similar to RMW (a different flag name) to make sure that + // we do not assign falseReg/targetReg same. Then, we would do something like this: + // + // ldnf1sh target, pg/z, [x0] + // sel mask, target, target, falseReg + // + // This needs more careful thinking, so disabling it for now. + // test.ConditionalSelect_FalseOp(); + + // Validates using inside ConditionalSelect with zero falseValue + test.ConditionalSelect_ZeroOp(); + } + 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 LoadUnaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {RetBaseType}[] outArray, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfoutArray) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfoutArray: {sizeOfoutArray}"); + } + + this.inArray1 = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{RetBaseType}> _fld1; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{RetBaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{RetBaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario(LoadUnaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(({Op1BaseType}*)testClass._dataTable.inArray1Ptr); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(testClass._dataTable.inArray1Ptr, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{RetVectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {RetBaseType}[] _maskData = new {RetBaseType}[RetElementCount]; + private static {RetBaseType}[] _falseData = new {RetBaseType}[RetElementCount]; + private static {Op1BaseType}[] _data = new {Op1BaseType}[Op1ElementCount]; + + private {Op1VectorType}<{RetBaseType}> _fld1; + private {Op1VectorType}<{RetBaseType}> _mask; + private {Op1VectorType}<{RetBaseType}> _falseFld; + + private DataTable _dataTable; + + public LoadUnaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < RetElementCount; i++) { _maskData[i] = ({RetBaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{RetBaseType}>, byte>(ref _mask), ref Unsafe.As<{RetBaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{RetBaseType}>>()); + + for (var i = 0; i < RetElementCount; i++) { _falseData[i] = ({RetBaseType})({NextValueOp1} % 50); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{RetBaseType}>, byte>(ref _falseFld), ref Unsafe.As<{RetBaseType}, byte>(ref _falseData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{RetBaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = {NextValueOp1}; } + _dataTable = new DataTable(_data, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + var result = {Isa}.{Method}( + ({Op1BaseType}*)(_dataTable.inArray1Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadNonFaulting() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_LoadNonFaulting)); + + var result = {Isa}.{Method}(default); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateZeroResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_Load)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1BaseType}*) }) + .Invoke(null, new object[] { + Pointer.Box(_dataTable.inArray1Ptr, typeof({Op1BaseType}*)) + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + _fld1 = {Isa}.{Method}(({Op1BaseType}*)_dataTable.inArray1Ptr); + + Unsafe.Write(_dataTable.outArrayPtr, _fld1); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + test._fld1 = {Isa}.{Method}(({Op1BaseType}*)_dataTable.inArray1Ptr); + + Unsafe.Write(_dataTable.outArrayPtr, test._fld1); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void ConditionalSelect_FalseOp() + { + TestLibrary.TestFramework.BeginScenario(nameof(ConditionalSelect_FalseOp)); + ConditionalSelectScenario(_mask, _dataTable.inArray1Ptr, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_zero"); + ConditionalSelectScenario({Op1VectorType}<{RetBaseType}>.Zero, _dataTable.inArray1Ptr, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_all"); + ConditionalSelectScenario({Op1VectorType}<{RetBaseType}>.AllBitsSet, _dataTable.inArray1Ptr, _falseFld); + } + + public void ConditionalSelect_ZeroOp() + { + TestLibrary.TestFramework.BeginScenario(nameof(ConditionalSelect_ZeroOp)); + ConditionalSelectScenario(_mask, _dataTable.inArray1Ptr, {Op1VectorType}<{RetBaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_zero"); + ConditionalSelectScenario({Op1VectorType}<{RetBaseType}>.Zero, _dataTable.inArray1Ptr, {Op1VectorType}<{RetBaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_all"); + ConditionalSelectScenario({Op1VectorType}<{RetBaseType}>.AllBitsSet, _dataTable.inArray1Ptr, {Op1VectorType}<{RetBaseType}>.Zero); + } + + [method: MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConditionalSelectScenario({Op1VectorType}<{RetBaseType}> mask, void* address, {Op1VectorType}<{RetBaseType}> falseOp) + { + var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(({Op1BaseType}*)address), falseOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectResult(mask, address, falseOp, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + Succeeded = false; + + try + { + RunBasicScenario_Load(); + } + catch (PlatformNotSupportedException) + { + Succeeded = true; + } + } + + private void ValidateConditionalSelectResult({Op1VectorType}<{RetBaseType}> maskOp, void* leftOp, {Op1VectorType}<{RetBaseType}> falseOp, void* output, [CallerMemberName] string method = "") + { + {RetBaseType}[] mask = new {RetBaseType}[RetElementCount]; + {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] falseVal = new {RetBaseType}[RetElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref mask[0]), maskOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), ref Unsafe.AsRef<byte>(leftOp), (uint)Unsafe.SizeOf<{RetVectorType}<{Op1BaseType}>>()); + Unsafe.WriteUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref falseVal[0]), falseOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref result[0]), ref Unsafe.AsRef<byte>(output), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + bool succeeded = true; + + for (var i = 0; i < RetElementCount; i++) + { + {RetBaseType} iterResult = (mask[i] != 0) ? (({RetBaseType})left[i]) : falseVal[i]; + if (iterResult != result[i]) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1BaseType}*): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", mask)})"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" falseOp: ({string.Join(", ", falseVal)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, outArray, method); + } + + private void ValidateResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), (uint)Unsafe.SizeOf<{RetVectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (var i = 0; i < RetElementCount; i++) + { + if ({ValidateIterResult}) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1BaseType}): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateZeroResult(void* firstOp, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), (uint)Unsafe.SizeOf<{RetVectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateZeroResult(inArray, outArray, method); + } + + private void ValidateZeroResult({Op1BaseType}[] firstOp, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (var i = 0; i < RetElementCount; i++) + { + if (result[i] != 0) + { + succeeded = false; + break; + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1BaseType}): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveSaturatingByActiveElementCount.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveSaturatingByActiveElementCount.template new file mode 100644 index 0000000000000..983ca31ddc54e --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveSaturatingByActiveElementCount.template @@ -0,0 +1,295 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new SaturatingByActiveTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class SaturatingByActiveTest__{TestName} + { + private struct DataTable + { + private byte[] inArray2; + + private GCHandle inHandle2; + + private ulong alignment; + + public DataTable({Op2BaseType}[] inArray2, int alignment) + { + int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf<{Op2BaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray2) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray2: {sizeOfinArray2}"); + } + + this.inArray2 = new byte[alignment * 2]; + + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), (uint)sizeOfinArray2); + } + + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle2.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1BaseType} _fld1; + public {Op2VectorType}<{Op2BaseType}> _fld2; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + testStruct._fld1 = {NextValueOp1}; + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario(SaturatingByActiveTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2); + + testClass.ValidateResult(_fld1, _fld2, result); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op2ElementCount = Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>() / sizeof({Op2BaseType}); + + private static {Op1BaseType} _data1; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private {Op1BaseType} _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + + private DataTable _dataTable; + + public SaturatingByActiveTest__{TestName}() + { + Succeeded = true; + + _fld1 = {NextValueOp1}; + _data1 = {NextValueOp1}; + + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new DataTable(_data2, 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.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + ); + + ValidateResult(_data1, _dataTable.inArray2Ptr, result); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op2VectorType}<{Op2BaseType}> loadMask = Sve.CreateTrueMask{Op2BaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)), + {LoadIsa}.Load{Op2VectorType}(loadMask, ({Op2BaseType}*)(_dataTable.inArray2Ptr)) + ); + + ValidateResult(_data1, _dataTable.inArray2Ptr, result); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1BaseType}), typeof({Op2VectorType}<{Op2BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + }); + + ValidateResult(_data1, _dataTable.inArray2Ptr, ({RetBaseType})result); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)); + var op2 = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var result = {Isa}.{Method}(op1, op2); + + ValidateResult(op1, op2, result); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2); + + ValidateResult(_fld1, _fld2, result); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, test._fld2); + + ValidateResult(test._fld1, test._fld2, result); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1BaseType} op1, {Op2VectorType}<{Op2BaseType}> op2, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), op2); + + ValidateResult(op1, inArray2, result, method); + } + + private void ValidateResult({Op1BaseType} op1, void* op2, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + ValidateResult(op1, inArray2, result, method); + } + + private void ValidateResult({Op1BaseType} left, {Op2BaseType}[] right, {RetBaseType} result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {ValidateResult} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}{RetBaseType}({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; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveVecReduceToScalarBinOpTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveVecReduceToScalarBinOpTest.template new file mode 100644 index 0000000000000..cc8bf7bbc4066 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveVecReduceToScalarBinOpTest.template @@ -0,0 +1,311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new ReduceToScalarBinaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class ReduceToScalarBinaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + private byte[] inArray2; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {Op2BaseType}[] inArray2, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf<{Op2BaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfinArray2: {sizeOfinArray2}"); + } + + this.inArray1 = new byte[alignment * 2]; + this.inArray2 = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), (uint)sizeOfinArray2); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op2VectorType}<{Op2BaseType}> _fld2; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario(ReduceToScalarBinaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2); + + testClass.ValidateResult(_fld1, _fld2, result); + } + } + + 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 {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + + private DataTable _dataTable; + + public ReduceToScalarBinaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new DataTable(_data1, _data2, 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) + ); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, result); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(loadMask, ({Op2BaseType}*)(_dataTable.inArray2Ptr)) + ); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, result); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + }); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, ({RetBaseType})result); + } + + 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); + + ValidateResult(op1, op2, result); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2); + + ValidateResult(_fld1, _fld2, result); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, test._fld2); + + ValidateResult(test._fld1, test._fld2, result); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), op2); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult(void* op1, void* op2, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef<byte>(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + ValidateResult(inArray1, inArray2, result, method); + } + + private void ValidateResult({Op1BaseType}[] left, {Op2BaseType}[] right, {RetBaseType} result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {ValidateResult} + + 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; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_2Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_2Test.template index 95574352689e2..34d7175a13bed 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_2Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_2Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_3Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_3Test.template index 9fc5bed8818ac..fe010a1e4cf3e 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_3Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_3Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_4Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_4Test.template index cf88f29ed5d9a..df3d5d8eb84f6 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_4Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookupExtension_4Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_2Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_2Test.template index 976885162379c..ad3e6619a520c 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_2Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_2Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_3Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_3Test.template index 1cf097dd61e24..ef18afcf3b146 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_3Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_3Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_4Test.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_4Test.template index d089eaf82365f..925b31789ad01 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_4Test.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/VectorLookup_4Test.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmBinaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmBinaryOpTestTemplate.template index 2563b1fd5c5e9..e6386689e2e66 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmBinaryOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmBinaryOpTestTemplate.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmOpTestTemplate.template index c197df15cf2cd..6c9455d61d387 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmOpTestTemplate.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmUnaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmUnaryOpTestTemplate.template index 71d1562a947b7..85fb5db27c2e6 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmUnaryOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_ImmUnaryOpTestTemplate.template @@ -3,7 +3,7 @@ /****************************************************************************** * 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 * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryMaskOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryMaskOpTestTemplate.template new file mode 100644 index 0000000000000..5acc3521c992f --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryMaskOpTestTemplate.template @@ -0,0 +1,424 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TemplateName}BinaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + + // Validates executing the test inside conditional, with op1 as falseValue + test.ConditionalSelect_Op1(); + + // Validates executing the test inside conditional, with op3 as falseValue + test.ConditionalSelect_FalseOp(); + + // Validates executing the test inside conditional, with op3 as zero + test.ConditionalSelect_ZeroOp(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class {TemplateName}BinaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + private byte[] inArray2; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {Op2BaseType}[] inArray2, {RetBaseType}[] outArray, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf<{Op2BaseType}>(); + int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfoutArray) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfinArray2: {sizeOfinArray2}, sizeOfoutArray: {sizeOfoutArray}"); + } + + this.inArray1 = new byte[alignment * 2]; + this.inArray2 = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), (uint)sizeOfinArray2); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op2VectorType}<{Op2BaseType}> _fld2; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario({TemplateName}BinaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int Op2ElementCount = Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>() / sizeof({Op2BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _maskData = new {Op1BaseType}[Op1ElementCount]; + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + private static {Op1BaseType}[] _falseData = new {Op1BaseType}[Op1ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _mask; + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + private {Op2VectorType}<{Op1BaseType}> _falseFld; + + private DataTable _dataTable; + + public {TemplateName}BinaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _mask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + for (var i = 0; i < Op1ElementCount; i++) { _falseData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _falseFld), ref Unsafe.As<{Op1BaseType}, byte>(ref _falseData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new DataTable(_data1, _data2, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask1 = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); + {Op2VectorType}<{Op2BaseType}> loadMask2 = Sve.CreateTrueMask{Op2BaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask1, ({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(loadMask2, ({Op2BaseType}*)(_dataTable.inArray2Ptr)) + ); + + 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}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var op2 = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var result = {Isa}.{Method}(op1, op2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, op2, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void ConditionalSelect_Op1() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_mask"); + ConditionalSelectScenario(_mask, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_zero"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_all"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _fld1); + } + + public void ConditionalSelect_FalseOp() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_mask"); + ConditionalSelectScenario(_mask, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_zero"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_all"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _falseFld); + } + + public void ConditionalSelect_ZeroOp() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_mask"); + ConditionalSelectScenario(_mask, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_zero"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_all"); + ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + } + + [method: MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConditionalSelectScenario({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op2BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp) + { + var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectResult(mask, op1, op2, falseOp, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateConditionalSelectResult({Op1VectorType}<{Op1BaseType}> maskOp, {Op1VectorType}<{Op1BaseType}> leftOp, {Op2VectorType}<{Op2BaseType}> rightOp, {Op1VectorType}<{Op1BaseType}> falseOp, void* output, [CallerMemberName] string method = "") + { + {Op1BaseType}[] mask = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] right = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] falseVal = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref mask[0]), maskOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref right[0]), rightOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref falseVal[0]), falseOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref result[0]), ref Unsafe.AsRef<byte>(output), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + bool succeeded = true; + + {TemplateValidationLogicForCndSel} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", mask)})"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" falseOp: ({string.Join(", ", falseVal)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void 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<byte>(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<byte>(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] left, {Op2BaseType}[] right, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {TemplateValidationLogic} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryOpTestTemplate.template index bd15acab428a0..0a10283036e83 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryOpTestTemplate.template @@ -9,6 +9,7 @@ ******************************************************************************/ using System; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImm2UnaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImm2UnaryOpTestTemplate.template new file mode 100644 index 0000000000000..697b5a6a01f6e --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImm2UnaryOpTestTemplate.template @@ -0,0 +1,359 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TemplateName}UnaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + + // Validates basic functionality fails with an invalid imm1, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm1(); + + // Validates basic functionality fails with an invalid imm2, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm2(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class {TemplateName}UnaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray; + private byte[] outArray; + + private GCHandle inHandle; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray, {RetBaseType}[] outArray, int alignment) + { + int sizeOfinArray = inArray.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); + + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray || (alignment * 2) < sizeOfoutArray) + { + throw new ArgumentException("Invalid value of alignment"); + } + + this.inArray = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; + + this.inHandle = GCHandle.Alloc(this.inArray, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArrayPtr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), (uint)sizeOfinArray); + } + + public void* inArrayPtr => Align((byte*)(inHandle.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld), ref Unsafe.As<{Op1BaseType}, byte>(ref _data[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario({TemplateName}UnaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld, {Imm}, {Imm2}); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld, {Imm}, {Imm2}, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + private static readonly {Op2BaseType} Imm = {Imm}; + private static readonly {Op3BaseType} Imm2 = {Imm2}; + + private static {Op1BaseType}[] _data = new {Op1BaseType}[Op1ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld; + + private DataTable _dataTable; + + public {TemplateName}UnaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld), ref Unsafe.As<{Op1BaseType}, byte>(ref _data[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data[i] = {NextValueOp1}; } + _dataTable = new DataTable(_data, 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.inArrayPtr), + {Imm}, + {Imm2} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, {Imm}, {Imm2}, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask, + ({Op1BaseType}*)(_dataTable.inArrayPtr)), + {Imm}, + {Imm2} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, {Imm}, {Imm2}, _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({Op2BaseType}), typeof({Op3BaseType}) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr), + {Imm}, + {Imm2}, + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArrayPtr, {Imm}, {Imm2}, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var firstOp = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr); + var result = {Isa}.{Method}(firstOp, {Imm}, {Imm2}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(firstOp, {Imm}, {Imm2}, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld, {Imm}, {Imm2}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld, {Imm}, {Imm2}, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld, {Imm}, {Imm2}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld, {Imm}, {Imm2}, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunBasicScenario_UnsafeRead_InvalidImm1() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm1)); + + bool succeeded = false; + + try + { + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr), + {InvalidImm}, + {Imm2} + ); + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + } + + public void RunBasicScenario_UnsafeRead_InvalidImm2() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm2)); + + bool succeeded = false; + + try + { + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr), + {Imm}, + {InvalidImm2} + ); + + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + } + + 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}> firstOp, {Op2BaseType} imm1, {Op3BaseType} imm2, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), firstOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray, imm1, imm2, outArray, method); + } + + private void ValidateResult(void* firstOp, {Op2BaseType} imm1, {Op3BaseType} imm2, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray[0]), ref Unsafe.AsRef<byte>(firstOp), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray, imm1, imm2, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {Op2BaseType} imm1, {Op3BaseType} imm2, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {TemplateValidationLogic} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Imm}, {Imm2}): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreAndZipTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMasklessBinaryOpTestTemplate.template similarity index 50% rename from src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreAndZipTest.template rename to src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMasklessBinaryOpTestTemplate.template index a80fb6a4482a0..c937e06494215 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreAndZipTest.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveMasklessBinaryOpTestTemplate.template @@ -3,14 +3,13 @@ /****************************************************************************** * This file is auto-generated from a template file by the GenerateTests.csx * - * script in tests\src\JIT\HardwareIntrinsics\Arm\Shared. In order to make * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * * changes, please update the corresponding template and run according to the * * directions listed in the file. * ******************************************************************************/ using System; using System.Numerics; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -24,14 +23,14 @@ namespace JIT.HardwareIntrinsics.Arm [Fact] public static void {TestName}() { - var test = new StoreAndZipTest__{TestName}(); + var test = new {TemplateName}BinaryOpTest__{TestName}(); if (test.IsSupported) { // Validates basic functionality works, using Unsafe.Read test.RunBasicScenario_UnsafeRead(); - if (Sve.IsSupported) + if ({LoadIsa}.IsSupported) { // Validates basic functionality works, using Load test.RunBasicScenario_Load(); @@ -65,68 +64,65 @@ namespace JIT.HardwareIntrinsics.Arm } } - public sealed unsafe class StoreAndZipTest__{TestName} + public sealed unsafe class {TemplateName}BinaryOpTest__{TestName} { private struct DataTable { private byte[] inArray1; + private byte[] inArray2; private byte[] outArray; - private byte[] maskArray; private GCHandle inHandle1; + private GCHandle inHandle2; private GCHandle outHandle; - private GCHandle maskHandle; private ulong alignment; - public DataTable({Op1BaseType}[] inArray1, {RetBaseType}[] outArray, {Op1BaseType}[] maskArray, int alignment) + public DataTable({Op1BaseType}[] inArray1, {Op2BaseType}[] inArray2, {RetBaseType}[] outArray, int alignment) { int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf<{Op2BaseType}>(); int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); - int sizeOfmaskArray = maskArray.Length * Unsafe.SizeOf<{Op1BaseType}>(); - - if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfoutArray || (alignment * 2) < sizeOfmaskArray) + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfoutArray) { - throw new ArgumentException("Invalid value of alignment"); + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfinArray2: {sizeOfinArray2}, sizeOfoutArray: {sizeOfoutArray}"); } - this.inArray1 = new byte[alignment * 2]; - this.outArray = new byte[alignment * 2]; - this.maskArray = new byte[alignment * 2]; + this.inArray1 = new byte[alignment * 2]; + this.inArray2 = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; - this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); - this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); - this.maskHandle = GCHandle.Alloc(this.maskArray, GCHandleType.Pinned); + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); this.alignment = (ulong)alignment; Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); - Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(maskArrayPtr), ref Unsafe.As<{Op1BaseType}, byte>(ref maskArray[0]), (uint)sizeOfmaskArray); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(inArray2Ptr), ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), (uint)sizeOfinArray2); } public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment); public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); - public void* maskArrayPtr => Align((byte*)(maskHandle.AddrOfPinnedObject().ToPointer()), alignment); public void Dispose() { inHandle1.Free(); + inHandle2.Free(); outHandle.Free(); - maskHandle.Free(); } - public static unsafe void* Align(byte* buffer, ulong expectedAlignment) + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) { return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); } } - private struct TestStruct { public {Op1VectorType}<{Op1BaseType}> _fld1; - public {Op1VectorType}<{Op1BaseType}> _storeMask; - private GCHandle _outHandle; + public {Op2VectorType}<{Op2BaseType}> _fld2; public static TestStruct Create() { @@ -134,53 +130,50 @@ namespace JIT.HardwareIntrinsics.Arm for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); - for (var i = 0; i < MaskElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } - Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._storeMask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); - - ulong alignment = (ulong)LargestVectorSize; - byte[] _outArray = new byte[alignment * 2]; - testStruct._outHandle = GCHandle.Alloc(_outArray, GCHandleType.Pinned); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); return testStruct; } - public void* _outArrayPtr => DataTable.Align((byte*)(_outHandle.AddrOfPinnedObject().ToPointer()), (ulong)LargestVectorSize); - - public void Dispose() - { - _outHandle.Free(); - } - - public void RunStructFldScenario(StoreAndZipTest__{TestName} testClass) + public void RunStructFldScenario({TemplateName}BinaryOpTest__{TestName} testClass) { - Sve.{Method}(_storeMask, ({Op1BaseType}*) _outArrayPtr, _fld1); + var result = {Isa}.{Method}(_fld1, _fld2); - testClass.ValidateResult(_fld1, _outArrayPtr, _storeMask); + 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 RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); - private static readonly int MaskElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + 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 {Op1BaseType}[] _maskData = new {Op1BaseType}[MaskElementCount]; + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; private DataTable _dataTable; - public StoreAndZipTest__{TestName}() + public {TemplateName}BinaryOpTest__{TestName}() { Succeeded = true; for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } - for (var i = 0; i < MaskElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); - _dataTable = new DataTable(_data1, new {RetBaseType}[RetElementCount], _maskData, LargestVectorSize); + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new DataTable(_data1, _data2, new {RetBaseType}[RetElementCount], LargestVectorSize); } - public bool IsSupported => Sve.IsSupported; + public bool IsSupported => {Isa}.IsSupported; public bool Succeeded { get; set; } @@ -188,55 +181,64 @@ namespace JIT.HardwareIntrinsics.Arm { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); - Sve.{Method}(Unsafe.Read<Vector<{Op1BaseType}>>(_dataTable.maskArrayPtr), ({Op1BaseType} *)_dataTable.outArrayPtr, Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr)); + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + ); - ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr, _dataTable.maskArrayPtr); + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); } public void RunBasicScenario_Load() { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); - {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); - Sve.{Method}(Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.maskArrayPtr)), ({Op1BaseType} *)_dataTable.outArrayPtr, Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr))); + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); - ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr, _dataTable.maskArrayPtr); + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(loadMask, ({Op2BaseType}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); } public void RunReflectionScenario_UnsafeRead() { TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); - typeof(Sve).GetMethod(nameof(Sve.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op1BaseType} *), typeof({Op1VectorType}<{Op1BaseType}>) }) - .Invoke(null, new object[] { Unsafe.Read<Vector<{Op1BaseType}>>(_dataTable.maskArrayPtr), - Pointer.Box(_dataTable.outArrayPtr, typeof({Op1BaseType}*)), Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr) + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) }); - ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr, _dataTable.maskArrayPtr); + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); } public void RunLclVarScenario_UnsafeRead() { TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); - {Op1VectorType}<{Op1BaseType}> op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); - {Op1VectorType}<{Op1BaseType}> storeMask = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.maskArrayPtr); - - Sve.{Method}(storeMask, ({Op1BaseType} *)_dataTable.outArrayPtr, op1); + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var op2 = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var result = {Isa}.{Method}(op1, op2); - ValidateResult(op1, _dataTable.outArrayPtr, storeMask); + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, op2, _dataTable.outArrayPtr); } public void RunClassFldScenario() { TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); - {Op1VectorType}<{Op1BaseType}> op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); - {Op1VectorType}<{Op1BaseType}> storeMask = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.maskArrayPtr); + var result = {Isa}.{Method}(_fld1, _fld2); - Sve.{Method}(storeMask, ({Op1BaseType} *)_dataTable.outArrayPtr, op1); - - ValidateResult(op1, _dataTable.outArrayPtr, storeMask); + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); } public void RunStructLclFldScenario() @@ -244,9 +246,10 @@ namespace JIT.HardwareIntrinsics.Arm TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); var test = TestStruct.Create(); - Sve.{Method}(test._storeMask, ({Op1BaseType} *)test._outArrayPtr, test._fld1); + var result = {Isa}.{Method}(test._fld1, test._fld2); - ValidateResult(test._fld1, test._outArrayPtr, test._storeMask); + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); } public void RunStructFldScenario() @@ -278,50 +281,43 @@ namespace JIT.HardwareIntrinsics.Arm } } - private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, void* result, {Op1VectorType}<{Op1BaseType}> storeMask, [CallerMemberName] string method = "") + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, void* result, [CallerMemberName] string method = "") { - {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; - {Op1BaseType}[] maskArray = new {Op1BaseType}[MaskElementCount]; + {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<{Op1BaseType}, byte>(ref maskArray[0]), storeMask); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), op2); Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); - ValidateResult(inArray1, outArray, maskArray, method); + ValidateResult(inArray1, inArray2, outArray, method); } - private void ValidateResult(void* op1, void* result, void* mask, [CallerMemberName] string method = "") + private void ValidateResult(void* op1, void* op2, void* result, [CallerMemberName] string method = "") { - {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; - {Op1BaseType}[] maskArray = new {Op1BaseType}[MaskElementCount]; + {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<byte>(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef<byte>(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef<byte>(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); - Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref maskArray[0]), ref Unsafe.AsRef<byte>(mask), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); - ValidateResult(inArray1, outArray, maskArray, method); + ValidateResult(inArray1, inArray2, outArray, method); } - private void ValidateResult({Op1BaseType}[] first, {RetBaseType}[] result, {Op1BaseType}[] maskArray, [CallerMemberName] string method = "") + private void ValidateResult({Op1BaseType}[] left, {Op2BaseType}[] right, {RetBaseType}[] result, [CallerMemberName] string method = "") { bool succeeded = true; - for (var i = 0; i < Op1ElementCount; i++) - { - if ({ValidateEntry}) - { - succeeded = false; - break; - } - } + {TemplateValidationLogic} if (!succeeded) { - TestLibrary.TestFramework.LogInformation($"{nameof(Sve)}.{nameof(Sve.{Method})}<{Op1BaseType}>({Op1VectorType}<{Op1BaseType}>, {Op1BaseType}*, {Op1VectorType}<{Op1BaseType}>): {method} failed:"); - TestLibrary.TestFramework.LogInformation($" first: ({string.Join(", ", first)})"); - TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", maskArray)})"); + 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); diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreNarrowingTest.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveStoreTemplate.template similarity index 92% rename from src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreNarrowingTest.template rename to src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveStoreTemplate.template index 4770ad827519d..d225124579d5b 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveStoreNarrowingTest.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveStoreTemplate.template @@ -24,7 +24,7 @@ namespace JIT.HardwareIntrinsics.Arm [Fact] public static void {TestName}() { - var test = new StoreNarrowingTest__{TestName}(); + var test = new {Method}Test__{TestName}(); if (test.IsSupported) { @@ -65,7 +65,7 @@ namespace JIT.HardwareIntrinsics.Arm } } - public sealed unsafe class StoreNarrowingTest__{TestName} + public sealed unsafe class {Method}Test__{TestName} { private struct DataTable { @@ -151,7 +151,7 @@ namespace JIT.HardwareIntrinsics.Arm _outHandle.Free(); } - public void RunStructFldScenario(StoreNarrowingTest__{TestName} testClass) + public void RunStructFldScenario({Method}Test__{TestName} testClass) { Sve.{Method}(_storeMask, ({Op2BaseType}*) _outArrayPtr, _fld1); @@ -170,7 +170,7 @@ namespace JIT.HardwareIntrinsics.Arm private DataTable _dataTable; - public StoreNarrowingTest__{TestName}() + public {Method}Test__{TestName}() { Succeeded = true; @@ -188,7 +188,7 @@ namespace JIT.HardwareIntrinsics.Arm { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); - Sve.{Method}(Unsafe.Read<Vector<{Op1BaseType}>>(_dataTable.maskArrayPtr), ({Op2BaseType} *)_dataTable.outArrayPtr, Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr)); + Sve.{Method}(Unsafe.Read<Vector<{Op1BaseType}>>(_dataTable.maskArrayPtr), ({Op2BaseType}*)_dataTable.outArrayPtr, Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr)); ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr, _dataTable.maskArrayPtr); } @@ -198,7 +198,7 @@ namespace JIT.HardwareIntrinsics.Arm TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); - Sve.{Method}(Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.maskArrayPtr)), ({Op2BaseType} *)_dataTable.outArrayPtr, Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr))); + Sve.{Method}(Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.maskArrayPtr)), ({Op2BaseType}*)_dataTable.outArrayPtr, Sve.LoadVector(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr))); ValidateResult(_dataTable.inArray1Ptr, _dataTable.outArrayPtr, _dataTable.maskArrayPtr); } @@ -207,7 +207,7 @@ namespace JIT.HardwareIntrinsics.Arm { TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); - typeof(Sve).GetMethod(nameof(Sve.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2BaseType} *), typeof({Op1VectorType}<{Op1BaseType}>) }) + typeof(Sve).GetMethod(nameof(Sve.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2BaseType}*), typeof({Op1VectorType}<{Op1BaseType}>) }) .Invoke(null, new object[] { Unsafe.Read<Vector<{Op1BaseType}>>(_dataTable.maskArrayPtr), Pointer.Box(_dataTable.outArrayPtr, typeof({Op2BaseType}*)), Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr) }); @@ -222,7 +222,7 @@ namespace JIT.HardwareIntrinsics.Arm {Op1VectorType}<{Op1BaseType}> op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); {Op1VectorType}<{Op1BaseType}> storeMask = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.maskArrayPtr); - Sve.{Method}(storeMask, ({Op2BaseType} *)_dataTable.outArrayPtr, op1); + Sve.{Method}(storeMask, ({Op2BaseType}*)_dataTable.outArrayPtr, op1); ValidateResult(op1, _dataTable.outArrayPtr, storeMask); } @@ -234,7 +234,7 @@ namespace JIT.HardwareIntrinsics.Arm {Op1VectorType}<{Op1BaseType}> op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); {Op1VectorType}<{Op1BaseType}> storeMask = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.maskArrayPtr); - Sve.{Method}(storeMask, ({Op2BaseType} *)_dataTable.outArrayPtr, op1); + Sve.{Method}(storeMask, ({Op2BaseType}*)_dataTable.outArrayPtr, op1); ValidateResult(op1, _dataTable.outArrayPtr, storeMask); } @@ -244,7 +244,7 @@ namespace JIT.HardwareIntrinsics.Arm TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); var test = TestStruct.Create(); - Sve.{Method}(test._storeMask, ({Op2BaseType} *)test._outArrayPtr, test._fld1); + Sve.{Method}(test._storeMask, ({Op2BaseType}*)test._outArrayPtr, test._fld1); ValidateResult(test._fld1, test._outArrayPtr, test._storeMask); } @@ -308,14 +308,7 @@ namespace JIT.HardwareIntrinsics.Arm { bool succeeded = true; - for (var i = 0; i < Op1ElementCount; i++) - { - if ({ValidateEntry}) - { - succeeded = false; - break; - } - } + {TemplateValidationLogic} if (!succeeded) { diff --git a/src/tests/JIT/IL_Conformance/Convert/TestConvertFromIntegral.csproj b/src/tests/JIT/IL_Conformance/Convert/TestConvertFromIntegral.csproj index 377bf381b5286..1ea4cce794a72 100644 --- a/src/tests/JIT/IL_Conformance/Convert/TestConvertFromIntegral.csproj +++ b/src/tests/JIT/IL_Conformance/Convert/TestConvertFromIntegral.csproj @@ -5,6 +5,8 @@ <PropertyGroup> <DebugType>None</DebugType> <Optimize>True</Optimize> + <!-- Test uses Reflection.Emit --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="$(MSBuildProjectName).cs" /> diff --git a/src/tests/JIT/Methodical/Coverage/arglist_pos.ilproj b/src/tests/JIT/Methodical/Coverage/arglist_pos.ilproj index 7e6379b9ea3b7..a4a72279d6f09 100644 --- a/src/tests/JIT/Methodical/Coverage/arglist_pos.ilproj +++ b/src/tests/JIT/Methodical/Coverage/arglist_pos.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>None</DebugType> diff --git a/src/tests/JIT/Methodical/refany/seq_d.ilproj b/src/tests/JIT/Methodical/refany/seq_d.ilproj index bdd50eb4ba0f5..89d9b802e26fd 100644 --- a/src/tests/JIT/Methodical/refany/seq_d.ilproj +++ b/src/tests/JIT/Methodical/refany/seq_d.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>Full</DebugType> diff --git a/src/tests/JIT/Methodical/refany/seq_r.ilproj b/src/tests/JIT/Methodical/refany/seq_r.ilproj index 579cbcc86cee1..76a250d1ed9b3 100644 --- a/src/tests/JIT/Methodical/refany/seq_r.ilproj +++ b/src/tests/JIT/Methodical/refany/seq_r.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-EJIT/V1-M12-Beta2/b26323/b26323.ilproj b/src/tests/JIT/Regression/CLR-x86-EJIT/V1-M12-Beta2/b26323/b26323.ilproj index 7c7ed9f1abff7..db1de8f8b9d83 100644 --- a/src/tests/JIT/Regression/CLR-x86-EJIT/V1-M12-Beta2/b26323/b26323.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-EJIT/V1-M12-Beta2/b26323/b26323.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b16423/b16423.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b16423/b16423.csproj index e3c43ccd56057..2ecda6a2ee3f9 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b16423/b16423.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b16423/b16423.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324a.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324a.ilproj index e32068b88eaf8..4a9783251c045 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324a.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324a.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324b.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324b.ilproj index 31d5a5cc06cd0..b3edc949acd04 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324b.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b26324/b26324b.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28901/b28901.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28901/b28901.ilproj index 444068cd5b60b..876a772e7ead8 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28901/b28901.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28901/b28901.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28927/b28927.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28927/b28927.ilproj index 7c7ed9f1abff7..db1de8f8b9d83 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28927/b28927.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b28927/b28927.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30838/b30838.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30838/b30838.ilproj index 444068cd5b60b..876a772e7ead8 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30838/b30838.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30838/b30838.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30864/b30864.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30864/b30864.ilproj index 444068cd5b60b..876a772e7ead8 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30864/b30864.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b30864/b30864.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32374/b32374.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32374/b32374.ilproj index 693ec752359ab..2e65bf674ce8e 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32374/b32374.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32374/b32374.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows, uses native varargs --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32879/b32879.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32879/b32879.ilproj index 7c7ed9f1abff7..42fb904e994c0 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32879/b32879.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M09.5-PDC/b32879/b32879.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- The test has a static field of unresolvable type on the class that defines Main() and native AOT makes Main() uncallable --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b35784/b35784.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b35784/b35784.ilproj index 444068cd5b60b..876a772e7ead8 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b35784/b35784.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b35784/b35784.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b36472/b36472.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b36472/b36472.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b36472/b36472.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b36472/b36472.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b37598/b37598.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b37598/b37598.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b37598/b37598.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b37598/b37598.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b41391/b41391.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b41391/b41391.csproj index e3c43ccd56057..2ecda6a2ee3f9 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b41391/b41391.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b41391/b41391.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b46867/b46867.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b46867/b46867.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b46867/b46867.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M11-Beta1/b46867/b46867.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31745/b31745.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31745/b31745.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31745/b31745.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31745/b31745.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31746/b31746.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31746/b31746.csproj index e3c43ccd56057..2ecda6a2ee3f9 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31746/b31746.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b31746/b31746.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b37646/b37646.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b37646/b37646.csproj index e3c43ccd56057..2ecda6a2ee3f9 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b37646/b37646.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b37646/b37646.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b41852/b41852.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b41852/b41852.csproj index e3c43ccd56057..2ecda6a2ee3f9 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b41852/b41852.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b41852/b41852.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b88793/b88793.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b88793/b88793.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b88793/b88793.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b88793/b88793.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b91248/b91248.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b91248/b91248.csproj index 56650eb0d6f0f..aa13294a233cf 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b91248/b91248.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b91248/b91248.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.ilproj index 7c7ed9f1abff7..d39bc208e6c8d 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.ilproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk.IL"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Modifies a value of an initonly field outside of cctor --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b399444/b399444.csproj b/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b399444/b399444.csproj index de4f07e83a8d0..a93881775de1d 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b399444/b399444.csproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b399444/b399444.csproj @@ -1,6 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <CLRTestPriority>1</CLRTestPriority> + <!-- Hits CodeView implementation limitations --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b409748/b409748.ilproj b/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b409748/b409748.ilproj index eca3fbb10b07a..e6136bb100ea3 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b409748/b409748.ilproj +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V2.0-Beta2/b409748/b409748.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows, uses native varargs --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>Full</DebugType> diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.cs b/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.cs new file mode 100644 index 0000000000000..9e3d82b0cff82 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.cs @@ -0,0 +1,42 @@ +// 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; + +// This takes the address of, gets the size of, or declares a pointer to a managed type +#pragma warning disable CS8500 + +public unsafe class Runtime_102577 +{ + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Problem(void* pDst, void* pSrc, object* pObj) + { + Unsafe.CopyBlock(pDst, pSrc, 1000); + StructWithManyObjs x = default; + if (pDst != null) + { + x.ObjFour = *pObj; + *(StructWithManyObjs*)pDst = x; + } + } + + private struct StructWithManyObjs + { + public object ObjOne; + public object ObjTwo; + public object ObjThree; + public object ObjFour; + public object ObjFive; + } + + [Fact] + public static void TestEntryPoint() + { + object x = null; + byte* p = stackalloc byte[1000]; + Random.Shared.NextBytes(new Span<byte>(p, 1000)); + Problem(p, p, &x); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.csproj new file mode 100644 index 0000000000000..9e9758ca6966a --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_102577/Runtime_102577.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <!-- Needed for CLRTestEnvironmentVariable --> + <RequiresProcessIsolation>true</RequiresProcessIsolation> + <Optimize>True</Optimize> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <ItemGroup> + <Compile Include="$(MSBuildProjectName).cs" /> + <!-- Make sure the managed helpers aren't prejitted for better coverage in GCStress modes --> + <CLRTestEnvironmentVariable Include="DOTNET_ReadyToRun" Value="0" /> + </ItemGroup> +</Project> diff --git a/src/tests/JIT/jit64/gc/misc/funclet.csproj b/src/tests/JIT/jit64/gc/misc/funclet.csproj index 2ab9db2b73a74..565ffb78ec324 100644 --- a/src/tests/JIT/jit64/gc/misc/funclet.csproj +++ b/src/tests/JIT/jit64/gc/misc/funclet.csproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i00.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i00.ilproj index aa54ffebd36c8..9749ec53d054a 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i00.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i00.ilproj @@ -3,6 +3,8 @@ <!-- Needed for CMakeProjectReference --> <RequiresProcessIsolation>true</RequiresProcessIsolation> <CLRTestPriority>1</CLRTestPriority> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i01.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i01.ilproj index 680dbb9065214..691869e712ddd 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i01.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i01.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i02.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i02.ilproj index 32be903e10139..a0b5d8f538a03 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i02.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i02.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i03.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i03.ilproj index 2dff157caa158..0224579e66ef2 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i03.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i03.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i10.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i10.ilproj index d008e784b8f3d..306d0b499f922 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i10.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i10.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i11.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i11.ilproj index a9c13f277b616..ba98b80aa7665 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i11.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i11.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i12.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i12.ilproj index 16458b097ccef..117aa6814babf 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i12.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i12.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i13.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i13.ilproj index 94bfb064feece..511895c52ea43 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i13.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i13.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i30.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i30.ilproj index 882cf6c65c16c..99c28bc2324c7 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i30.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i30.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i31.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i31.ilproj index 791b405d12dd9..105b6a8e4faf8 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i31.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i31.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i32.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i32.ilproj index d3062cab3dabb..b5283a6b219d9 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i32.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i32.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i33.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i33.ilproj index 3be58daf3e9be..1e0976c95b068 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i33.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i33.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i50.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i50.ilproj index f56e79db105d3..e95f831a0d493 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i50.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i50.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i51.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i51.ilproj index 4a3c0d7200ef9..94aab25491897 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i51.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i51.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i52.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i52.ilproj index 24a88519aff5e..b5038c2ae7132 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i52.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i52.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i53.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i53.ilproj index f42d378e6bb59..02577149420d0 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i53.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i53.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i60.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i60.ilproj index 74efc070b0d31..c78b8b3513821 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i60.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i60.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows uses native varargs --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i61.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i61.ilproj index 1edb2fb6f6293..ec740ee71adcd 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i61.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i61.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i62.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i62.ilproj index 89253a4f5d333..d6cf1b65153bf 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i62.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i62.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i63.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i63.ilproj index afa4d272f48a5..2ff78b81dd27e 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i63.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i63.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i70.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i70.ilproj index 2bccb1565233f..0faf21ed41c4f 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i70.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i70.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i71.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i71.ilproj index f3225a5159ca3..a2e7a3e789681 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i71.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i71.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i72.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i72.ilproj index 8b0b7da0f33fd..dd4b1728212a6 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i72.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i72.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i73.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i73.ilproj index bfcbc42af3ada..84cd2b6d03c81 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i73.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i73.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i80.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i80.ilproj index a12ff82bb549f..96572d9f14809 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i80.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i80.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i81.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i81.ilproj index a16f966828cee..780990e30744f 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i81.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i81.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i82.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i82.ilproj index 936f92032108c..5d2b0ae7076ef 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i82.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i82.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/jit64/mcc/interop/mcc_i83.ilproj b/src/tests/JIT/jit64/mcc/interop/mcc_i83.ilproj index bed6ac827aaf9..4b44051c04487 100644 --- a/src/tests/JIT/jit64/mcc/interop/mcc_i83.ilproj +++ b/src/tests/JIT/jit64/mcc/interop/mcc_i83.ilproj @@ -5,6 +5,8 @@ <CLRTestPriority>1</CLRTestPriority> <!-- Test unsupported outside of windows --> <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported> + <!-- Test uses varargs --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <PropertyGroup> <DebugType>PdbOnly</DebugType> diff --git a/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.cs b/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.cs new file mode 100644 index 0000000000000..f5b3e7898a1a3 --- /dev/null +++ b/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.cs @@ -0,0 +1,36 @@ +// 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 unsafe class LocalMorphBackedge +{ + [Fact] + public static int TestEntryPoint() + { + int x = 1234; + int y = 5678; + int* px; + int** ppx = null; + + for (int i = 100; i < GetUpper(); i++) + { + px = &x; + + if (ppx != null) + { + *ppx = &y; + } + + *px = i; + ppx = &px; + } + + return x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int GetUpper() => 102; +} \ No newline at end of file diff --git a/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.csproj b/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.csproj new file mode 100644 index 0000000000000..501217e4d8689 --- /dev/null +++ b/src/tests/JIT/opt/AssertionPropagation/LocalMorphBackedge.csproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <DebugType>None</DebugType> + <Optimize>True</Optimize> + </PropertyGroup> + <ItemGroup> + <Compile Include="$(MSBuildProjectName).cs" /> + </ItemGroup> +</Project> diff --git a/src/tests/JIT/opt/Loops/TripCountOverflow.cs b/src/tests/JIT/opt/Loops/TripCountOverflow.cs new file mode 100644 index 0000000000000..8f30181ce3bbd --- /dev/null +++ b/src/tests/JIT/opt/Loops/TripCountOverflow.cs @@ -0,0 +1,40 @@ +// 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; +using Xunit; + +public unsafe class TripCountOverflow +{ + [Fact] + public static void TestEntryPoint() + { + Assert.Throws<Exception>(() => InfiniteLoopExitedByException((long)int.MaxValue + 1, (long)int.MaxValue + 5)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long InfiniteLoopExitedByException(long n, long k) + { + // Since the caller passes int.MaxValue + 1 this loop is infinite, so we shouldn't be able to analyze + // its trip count (without doing some form of cloning). + long sum = 0; + for (int i = 0; i < n; i++) + { + sum += 3; + + if (k-- == 0) + { + sum += Foo(); + } + } + + return sum; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Foo() + { + throw new Exception(); + } +} diff --git a/src/tests/JIT/opt/Loops/TripCountOverflow.csproj b/src/tests/JIT/opt/Loops/TripCountOverflow.csproj new file mode 100644 index 0000000000000..4ee2e5892f225 --- /dev/null +++ b/src/tests/JIT/opt/Loops/TripCountOverflow.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <CLRTestPriority>1</CLRTestPriority> + </PropertyGroup> + <PropertyGroup> + <DebugType>PdbOnly</DebugType> + <Optimize>True</Optimize> + </PropertyGroup> + <ItemGroup> + <Compile Include="$(MSBuildProjectName).cs" /> + </ItemGroup> +</Project> diff --git a/src/tests/Loader/binding/tracing/BinderTracingTest.targets b/src/tests/Loader/binding/tracing/BinderTracingTest.targets index c2165d7c89e60..063141b8c4c6d 100644 --- a/src/tests/Loader/binding/tracing/BinderTracingTest.targets +++ b/src/tests/Loader/binding/tracing/BinderTracingTest.targets @@ -10,6 +10,8 @@ <NativeAotIncompatible>true</NativeAotIncompatible> <!-- Tracing tests routinely time out with gcstress --> <GCStressIncompatible>true</GCStressIncompatible> + <!-- And they also time out with jitstress modes --> + <JitOptimizationSensitive>true</JitOptimizationSensitive> </PropertyGroup> <ItemGroup> <Compile Include="BinderTracingTest.cs" /> diff --git a/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs b/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs index 867cccd32d5be..45de0af7d211b 100644 --- a/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs +++ b/src/tests/Loader/classloader/generics/ByRefLike/Validate.cs @@ -11,6 +11,17 @@ public class Validate { + [Fact] + public static void Validate_Activation() + { + Console.WriteLine($"{nameof(Validate_Activation)}..."); + + Assert.Equal("System.Span<Int32>[0]", Activator.CreateInstance<Span<int>>().ToString()); + Assert.Equal("System.Span<String>[0]", Activator.CreateInstance<Span<string>>().ToString()); + Assert.Equal("System.ReadOnlySpan<Int32>[0]", Activator.CreateInstance<ReadOnlySpan<int>>().ToString()); + Assert.Equal("System.ReadOnlySpan<String>[0]", Activator.CreateInstance<ReadOnlySpan<string>>().ToString()); + } + [Fact] public static void Validate_TypeLoad() { diff --git a/src/tests/Loader/classloader/generics/Variance/Delegates/Delegates002.csproj b/src/tests/Loader/classloader/generics/Variance/Delegates/Delegates002.csproj index b5819a5b18846..144b6f0db1067 100644 --- a/src/tests/Loader/classloader/generics/Variance/Delegates/Delegates002.csproj +++ b/src/tests/Loader/classloader/generics/Variance/Delegates/Delegates002.csproj @@ -2,6 +2,8 @@ <PropertyGroup> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="Delegates002.cs" /> diff --git a/src/tests/Loader/classloader/generics/Variance/Interfaces/Interfaces001.csproj b/src/tests/Loader/classloader/generics/Variance/Interfaces/Interfaces001.csproj index f5efc4b19eb54..81ca6d01598fc 100644 --- a/src/tests/Loader/classloader/generics/Variance/Interfaces/Interfaces001.csproj +++ b/src/tests/Loader/classloader/generics/Variance/Interfaces/Interfaces001.csproj @@ -2,6 +2,8 @@ <PropertyGroup> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="Interfaces001.cs" /> diff --git a/src/tests/Loader/classloader/generics/Variance/Methods/Method002.csproj b/src/tests/Loader/classloader/generics/Variance/Methods/Method002.csproj index 5c9dc6a58257c..9429db37d0361 100644 --- a/src/tests/Loader/classloader/generics/Variance/Methods/Method002.csproj +++ b/src/tests/Loader/classloader/generics/Variance/Methods/Method002.csproj @@ -4,6 +4,8 @@ <RequiresProcessIsolation>true</RequiresProcessIsolation> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="Method002.cs" /> diff --git a/src/tests/Loader/classloader/generics/Variance/Methods/Method004.csproj b/src/tests/Loader/classloader/generics/Variance/Methods/Method004.csproj index e0dd2b527b885..f6ab270eeba01 100644 --- a/src/tests/Loader/classloader/generics/Variance/Methods/Method004.csproj +++ b/src/tests/Loader/classloader/generics/Variance/Methods/Method004.csproj @@ -4,6 +4,8 @@ <RequiresProcessIsolation>true</RequiresProcessIsolation> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="Method004.cs" /> diff --git a/src/tests/Loader/classloader/generics/regressions/109968/test_109968.ilproj b/src/tests/Loader/classloader/generics/regressions/109968/test_109968.ilproj index 892982d7b83bf..9bb7ec942b3fc 100644 --- a/src/tests/Loader/classloader/generics/regressions/109968/test_109968.ilproj +++ b/src/tests/Loader/classloader/generics/regressions/109968/test_109968.ilproj @@ -3,6 +3,8 @@ <!-- Needed for mechanical merging of all remaining tests, this particular project may not actually need process isolation --> <RequiresProcessIsolation>true</RequiresProcessIsolation> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="test_109968.il" /> diff --git a/src/tests/Loader/classloader/regressions/dev10_403582/dev10_403582.ilproj b/src/tests/Loader/classloader/regressions/dev10_403582/dev10_403582.ilproj index 20b494aa90652..d324ef887a1b7 100644 --- a/src/tests/Loader/classloader/regressions/dev10_403582/dev10_403582.ilproj +++ b/src/tests/Loader/classloader/regressions/dev10_403582/dev10_403582.ilproj @@ -3,6 +3,8 @@ <!-- Needed for mechanical merging of all remaining tests, this particular project may not actually need process isolation --> <RequiresProcessIsolation>true</RequiresProcessIsolation> <CLRTestPriority>1</CLRTestPriority> + <!-- Testing various TypeLoadExceptions --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="$(MSBuildProjectName).il" /> diff --git a/src/tests/Regressions/coreclr/GitHub_89834/test89834.csproj b/src/tests/Regressions/coreclr/GitHub_89834/test89834.csproj index 4ebefb68769a2..faaa85e7fd113 100644 --- a/src/tests/Regressions/coreclr/GitHub_89834/test89834.csproj +++ b/src/tests/Regressions/coreclr/GitHub_89834/test89834.csproj @@ -6,6 +6,8 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Relies on StackFrame.GetMethod that is not trim-compatible --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="test89834.cs" /> diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs index c64306a27bdcb..d0a40d59c011c 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs @@ -13,11 +13,17 @@ struct Struct { } public static unsafe class UnsafeAccessorsTestsGenerics { + class ClassWithEnum<T> + { + public enum Enum { } + } + class MyList<T> { public const string StaticGenericFieldName = nameof(_GF); public const string StaticFieldName = nameof(_F); public const string GenericFieldName = nameof(_list); + public const string GenericEnumFieldName = nameof(_enum); static MyList() { @@ -29,6 +35,7 @@ static MyList() private static string _F; private List<T> _list; + private ClassWithEnum<T>.Enum _enum; public MyList() => _list = new(); @@ -62,6 +69,57 @@ private void Add(Struct a) => public int Capacity => _list.Capacity; } + static class Accessors<V> + { + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + public extern static MyList<V> Create(int a); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + public extern static MyList<V> CreateWithList(List<V> a); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = ".ctor")] + public extern static void CallCtorAsMethod(MyList<V> l, List<V> a); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] + public extern static void AddInt(MyList<V> l, int a); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] + public extern static void AddString(MyList<V> l, string a); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] + public extern static void AddStruct(MyList<V> l, Struct a); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Clear")] + public extern static void Clear(MyList<V> l); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] + public extern static void Add(MyList<V> l, V element); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "AddWithIgnore")] + public extern static void AddWithIgnore<W>(MyList<V> l, V element, W ignore); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CanCastToElementType")] + public extern static bool CanCastToElementType<W>(MyList<V> l, W element); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CreateMessage")] + public extern static string CreateMessage(GenericBase<V> b, V v); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ElementType")] + public extern static Type ElementType(MyList<V> l); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CanUseElementType")] + public extern static bool CanUseElementType<W>(MyList<V> l, W element); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name=MyList<object>.GenericFieldName)] + public extern static ref List<V> GetPrivateField(MyList<V> a); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name=MyList<int>.GenericEnumFieldName)] + public extern static ref ClassWithEnum<V>.Enum GetPrivateEnumField(MyList<V> d); + + [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=MyList<int>.StaticGenericFieldName)] + public extern static ref V GetPrivateStaticField(MyList<V> d); + } + [Fact] public static void Verify_Generic_AccessStaticFieldClass() { @@ -76,12 +134,12 @@ public static void Verify_Generic_AccessStaticFieldClass() { int expected = 10; MyList<int>.SetStaticGenericField(expected); - Assert.Equal(expected, GetPrivateStaticField((MyList<int>)null)); + Assert.Equal(expected, Accessors<int>.GetPrivateStaticField((MyList<int>)null)); } { string expected = "abc"; MyList<string>.SetStaticGenericField(expected); - Assert.Equal(expected, GetPrivateStaticField((MyList<string>)null)); + Assert.Equal(expected, Accessors<string>.GetPrivateStaticField((MyList<string>)null)); } [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=MyList<int>.StaticFieldName)] @@ -92,9 +150,6 @@ public static void Verify_Generic_AccessStaticFieldClass() [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=MyList<Struct>.StaticFieldName)] extern static ref string GetPrivateStaticFieldStruct(MyList<Struct> d); - - [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=MyList<int>.StaticGenericFieldName)] - extern static ref V GetPrivateStaticField<V>(MyList<V> d); } [Fact] @@ -103,19 +158,19 @@ public static void Verify_Generic_AccessFieldClass() Console.WriteLine($"Running {nameof(Verify_Generic_AccessFieldClass)}"); { MyList<int> a = new(); - Assert.NotNull(GetPrivateField(a)); + Assert.NotNull(Accessors<int>.GetPrivateField(a)); + Accessors<int>.GetPrivateEnumField(a) = default; } { MyList<string> a = new(); - Assert.NotNull(GetPrivateField(a)); + Assert.NotNull(Accessors<string>.GetPrivateField(a)); + Accessors<string>.GetPrivateEnumField(a) = default; } { MyList<Struct> a = new(); - Assert.NotNull(GetPrivateField(a)); + Assert.NotNull(Accessors<Struct>.GetPrivateField(a)); + Accessors<Struct>.GetPrivateEnumField(a) = default; } - - [UnsafeAccessor(UnsafeAccessorKind.Field, Name=MyList<object>.GenericFieldName)] - extern static ref List<V> GetPrivateField<V>(MyList<V> a); } class Base @@ -186,48 +241,6 @@ public static void Verify_Generic_InheritanceMethodResolution() extern static string CreateMessage<W>(Base b, W w); } - sealed class Accessors<V> - { - [UnsafeAccessor(UnsafeAccessorKind.Constructor)] - public extern static MyList<V> Create(int a); - - [UnsafeAccessor(UnsafeAccessorKind.Constructor)] - public extern static MyList<V> CreateWithList(List<V> a); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = ".ctor")] - public extern static void CallCtorAsMethod(MyList<V> l, List<V> a); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] - public extern static void AddInt(MyList<V> l, int a); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] - public extern static void AddString(MyList<V> l, string a); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] - public extern static void AddStruct(MyList<V> l, Struct a); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Clear")] - public extern static void Clear(MyList<V> l); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Add")] - public extern static void Add(MyList<V> l, V element); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "AddWithIgnore")] - public extern static void AddWithIgnore<W>(MyList<V> l, V element, W ignore); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CanCastToElementType")] - public extern static bool CanCastToElementType<W>(MyList<V> l, W element); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CreateMessage")] - public extern static string CreateMessage(GenericBase<V> b, V v); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ElementType")] - public extern static Type ElementType(MyList<V> l); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CanUseElementType")] - public extern static bool CanUseElementType<W>(MyList<V> l, W element); - } - [Fact] public static void Verify_Generic_CallCtor() { @@ -393,7 +406,7 @@ private static string SM<T, U>() where T : U, IEquatable<T> } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/89439", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/102942", TestRuntimes.Mono)] public static void Verify_Generic_ConstraintEnforcement() { Console.WriteLine($"Running {nameof(Verify_Generic_ConstraintEnforcement)}"); diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 30f65993da6cc..72821f396b4f2 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -215,6 +215,119 @@ public static void Verify_AccessFieldValue() extern static ref string GetPrivateField(ref UserDataValue d); } + unsafe struct AllFields + { + private bool _bool; + private char _char; + private byte _byte; + private sbyte _sbyte; + private short _short; + private ushort _ushort; + private int _int; + private uint _uint; + private long _long; + private ulong _ulong; + private string _string; + private AttributeTargets _enum; + private void* _ptr; + private Guid _guid; + private object _object; + private int[] _array; + private int[,] _mdarray; + private IntPtr _intptr; + private UIntPtr _uintptr; + private delegate*<void> _fptr; + } + + [Fact] + public static void Verify_AccessAllFields_CorElementType() + { + Console.WriteLine($"Running {nameof(Verify_AccessAllFields_CorElementType)}"); + + AllFields allFields = default; + + GetBool(ref allFields) = default; + GetChar(ref allFields) = default; + GetByte(ref allFields) = default; + GetSByte(ref allFields) = default; + GetShort(ref allFields) = default; + GetUShort(ref allFields) = default; + GetInt(ref allFields) = default; + GetUInt(ref allFields) = default; + GetLong(ref allFields) = default; + GetULong(ref allFields) = default; + GetString(ref allFields) = default; + GetEnum(ref allFields) = default; + GetPtr(ref allFields) = default; + GetGuid(ref allFields) = default; + GetObject(ref allFields) = default; + GetArray(ref allFields) = default; + GetMDArray(ref allFields) = default; + GetIntPtr(ref allFields) = default; + GetUIntPtr(ref allFields) = default; + GetFPtr(ref allFields) = default; + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_bool")] + extern static ref bool GetBool(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_char")] + extern static ref char GetChar(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_byte")] + extern static ref byte GetByte(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_sbyte")] + extern static ref sbyte GetSByte(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_short")] + extern static ref short GetShort(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_ushort")] + extern static ref ushort GetUShort(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_int")] + extern static ref int GetInt(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_uint")] + extern static ref uint GetUInt(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_long")] + extern static ref long GetLong(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_ulong")] + extern static ref ulong GetULong(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_string")] + extern static ref string GetString(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_enum")] + extern static ref AttributeTargets GetEnum(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_ptr")] + extern static ref void* GetPtr(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_guid")] + extern static ref Guid GetGuid(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_object")] + extern static ref object GetObject(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_array")] + extern static ref int[] GetArray(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_mdarray")] + extern static ref int[,] GetMDArray(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_intptr")] + extern static ref IntPtr GetIntPtr(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_uintptr")] + extern static ref UIntPtr GetUIntPtr(ref AllFields f); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_fptr")] + extern static ref delegate*<void> GetFPtr(ref AllFields f); + } + [Fact] public static void Verify_AccessStaticMethodClass() { diff --git a/src/tests/build.cmd b/src/tests/build.cmd index eaa552d89a1d0..248863dcd77bc 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -114,6 +114,7 @@ if /i "%arg%" == "Perfmap" (set __CreatePerfmap=1&set processedArg if /i "%arg%" == "AllTargets" (set "__BuildNeedTargetArg=/p:CLRTestBuildAllTargets=allTargets"&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%arg%" == "ExcludeMonoFailures" (set __Mono=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%arg%" == "Mono" (set __Mono=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%arg%" == "CoreCLR" (set __Mono=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) @REM The following arguments also consume one subsequent argument if /i "%arg%" == "test" (set __BuildTestProject=!__BuildTestProject!%2%%3B&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) diff --git a/src/tests/build.sh b/src/tests/build.sh index f222c69988c78..42689057fa723 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -301,6 +301,12 @@ handle_arguments_local() { __SkipNative=1 ;; + coreclr|-coreclr) + __Mono=0 + __MonoAot=0 + __MonoFullAot=0 + ;; + log*|-log*) local arg="$1" local parts=(${arg//:/ }) diff --git a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs index d804dd63a0f4c..e4089f9812e37 100644 --- a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs +++ b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs @@ -145,12 +145,14 @@ public void TestPortablePdbMethodDebugInformation1() // Tests whether the portable PDB has appropriate sequence points defined // The test source file includes external source reference and thus has 2 variants depending on OS type - [Fact] - public void TestPortablePdbMethodDebugInformation2() + [Theory] + [InlineData("TestMethodDebugInformation")] + [InlineData("TestDocuments1")] + public void TestPortablePdbMethodDebugInformation2(string testName) { - var ilSource = IsUnix ? "TestMethodDebugInformation_unix.il" : "TestMethodDebugInformation_win.il"; + var ilSource = testName + (IsUnix ? "_unix.il" : "_win.il"); - var expected = IlasmPortablePdbTesterCommon.GetExpectedForTestMethodDebugInformation(ilSource); + var expected = IlasmPortablePdbTesterCommon.GetExpectedForTestMethodDebugInformation(testName, IsUnix); var ilasm = IlasmPortablePdbTesterCommon.GetIlasmFullPath(CoreRootVar, IlasmFile); IlasmPortablePdbTesterCommon.Assemble(ilasm, ilSource, TestDir, out string dll, out string pdb); @@ -174,10 +176,17 @@ public void TestPortablePdbMethodDebugInformation2() // verify method debug information from portable pdb metadata var methodDebugInformation = portablePdbMdReader.GetMethodDebugInformation(methodDefinitionHandle); - var methodDocument = portablePdbMdReader.GetDocument(methodDebugInformation.Document); - var methodDocumentName = portablePdbMdReader.GetString(methodDocument.Name); - Assert.Equal(expectedMethodDbgInfo.Document.Name, methodDocumentName); + if (expectedMethodDbgInfo.Document == null) + { + Assert.True(methodDebugInformation.Document.IsNil); + } + else + { + var methodDocument = portablePdbMdReader.GetDocument(methodDebugInformation.Document); + var methodDocumentName = portablePdbMdReader.GetString(methodDocument.Name); + Assert.Equal(expectedMethodDbgInfo.Document.Name, methodDocumentName); + } int i = 0; foreach (var sequencePoint in methodDebugInformation.GetSequencePoints()) { diff --git a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTesterCommon.cs b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTesterCommon.cs index 1c535fc802478..f31fb44f5f06b 100644 --- a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTesterCommon.cs +++ b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTesterCommon.cs @@ -94,7 +94,7 @@ public static List<DocumentStub> GetExpectedDocuments(string testName, string te } } - public static Dictionary<string, MethodDebugInformationStub> GetExpectedForTestMethodDebugInformation(string testName) + public static Dictionary<string, MethodDebugInformationStub> GetExpectedForTestMethodDebugInformation(string testName, bool isUnix) { string method1; string method2; @@ -103,39 +103,38 @@ public static Dictionary<string, MethodDebugInformationStub> GetExpectedForTestM switch (testName) { - case "TestMethodDebugInformation_unix.il": + case "TestMethodDebugInformation": method1 = ".ctor"; method2 = "Pow"; - document1 = new DocumentStub("/tmp/TestMethodDebugInformation/SimpleMath.cs"); - document2 = new DocumentStub("/tmp/TestMethodDebugInformation/SimpleMathMethods.cs"); + document1 = isUnix ? new DocumentStub("/tmp/TestMethodDebugInformation/SimpleMath.cs") : new DocumentStub("C:\\tmp\\TestMethodDebugInformation\\SimpleMath.cs"); + document2 = isUnix ? new DocumentStub("/tmp/TestMethodDebugInformation/SimpleMathMethods.cs") : new DocumentStub("C:\\tmp\\TestMethodDebugInformation\\SimpleMathMethods.cs"); break; - case "TestMethodDebugInformation_win.il": - method1 = ".ctor"; - method2 = "Pow"; - document1 = new DocumentStub("C:\\tmp\\TestMethodDebugInformation\\SimpleMath.cs"); - document2 = new DocumentStub("C:\\tmp\\TestMethodDebugInformation\\SimpleMathMethods.cs"); + case "TestDocuments1": + method1 = "Foo"; + method2 = null; + document1 = isUnix ? new DocumentStub("/tmp/non_existent_source1.cs") : new DocumentStub("C:\\tmp\\non_existent_source1.cs"); + document2 = isUnix ? new DocumentStub("/tmp/non_existent_source2.cs") : new DocumentStub("C:\\tmp\\non_existent_source2.cs"); break; default: Assert.Fail(); return null; } - return new Dictionary<string, MethodDebugInformationStub>() + var result = new Dictionary<string, MethodDebugInformationStub>(); + switch (testName) { - { - method1, - new MethodDebugInformationStub(method1, document1, + case "TestMethodDebugInformation": + result.Add(method1, + new MethodDebugInformationStub(method1, document1, new List<SequencePointStub>() { new SequencePointStub(document1, false, 0x0, 6, 6, 9, 37), new SequencePointStub(document1, false, 0x7, 7, 7, 9, 10), new SequencePointStub(document1, false, 0x8, 8, 8, 13, 28), new SequencePointStub(document1, false, 0xf, 9, 9, 9, 10), - }) - }, - { - method2, - new MethodDebugInformationStub(method2, document2, + })); + result.Add(method2, + new MethodDebugInformationStub(method2, document2, new List<SequencePointStub>() { new SequencePointStub(document2, false, 0x0, 6, 6, 9, 10), @@ -153,9 +152,19 @@ public static Dictionary<string, MethodDebugInformationStub> GetExpectedForTestM new SequencePointStub(document2, true, 0x1e), new SequencePointStub(document2, false, 0x21, 15, 15, 13, 24), new SequencePointStub(document2, false, 0x26, 16, 16, 9, 10), - }) - } + })); + break; + case "TestDocuments1": + result.Add(method1, + new MethodDebugInformationStub(method1, null, + new List<SequencePointStub>() + { + new SequencePointStub(document1, false, 0x0, 1, 1, 9, 10), + new SequencePointStub(document2, false, 0x1, 2, 2, 9, 10), + })); + break; }; + return result; } public static List<LocalScopeStub> GetExpectedForTestLocalScopes(string testName) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 44bad4357b928..d54f5662e806d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -304,6 +304,9 @@ <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/DictionaryExpansion/DictionaryExpansion/*"> <Issue>https://github.com/dotnet/runtime/issues/75244</Issue> </ExcludeList> + <ExcludeList Include="$(XunitTestBinBase)/Regressions/coreclr/0582/csgen.1/*"> + <Issue>https://github.com/dotnet/runtime/issues/102296</Issue> + </ExcludeList> </ItemGroup> <!-- Windows arm64 specific excludes --> @@ -712,6 +715,9 @@ <ExcludeList Include="$(XunitTestBinBase)/baseservices/exceptions/unhandled/unhandledTester/*"> <Issue>Test expects being run with corerun</Issue> </ExcludeList> + <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/regressions/dd52/**"> + <Issue>https://github.com/dotnet/runtime/issues/102741</Issue> + </ExcludeList> <ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/debugging/debuginfo/tester/*"> <Issue>Just-in-time compilation test</Issue> </ExcludeList> @@ -781,9 +787,15 @@ <ExcludeList Include="$(XunitTestBinBase)/Interop/SuppressGCTransition/SuppressGCTransitionTest/*"> <Issue>https://github.com/dotnet/runtimelab/issues/165</Issue> </ExcludeList> + <ExcludeList Include="$(XunitTestBinBase)/Interop/SuppressGCTransition/SuppressGCTransitionNegativeTest/*"> + <Issue>https://github.com/dotnet/runtimelab/issues/165</Issue> + </ExcludeList> <ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/BestFitMapping/**/*"> <Issue>https://github.com/dotnet/runtimelab/issues/172</Issue> </ExcludeList> + <ExcludeList Include="$(XunitTestBinBase)/Interop/BestFitMapping/**/*"> + <Issue>https://github.com/dotnet/runtimelab/issues/172</Issue> + </ExcludeList> <ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/CriticalHandles/**/*"> <Issue>https://github.com/dotnet/runtimelab/issues/173</Issue> </ExcludeList> @@ -1088,6 +1100,7 @@ <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader\DefaultInterfaceMethods\constrainedcall\constrainedcall\*" /> <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader\DefaultInterfaceMethods\constrainedcall\constrained2\*" /> <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader\DefaultInterfaceMethods\constrainedcall\constrained2_gm\*" /> + <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/regressions/dev10_568786/4_Misc/**" /> <!-- Arrays with non-zero lower bounds --> <!-- https://github.com/dotnet/runtimelab/issues/155 --> @@ -1098,10 +1111,20 @@ <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_r8_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_struct_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_u8_r\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_b_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_i4_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_objref_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_r4_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_r8_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge\huge_struct_d\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge\huge_struct\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\huge\huge_u8_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\address_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\ldelem_get_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\address_r\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\arrres_il_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\gcarr_il_r\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\gcarr_il_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\misc\ldelem_get_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\range\float64_range1_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\range\float64_range2_d\*" /> @@ -1118,6 +1141,7 @@ <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\range\int32_range1_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\Arrays\range\int32_range2_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\int64\arrays\hugedim_r\*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\int64\arrays\hugedim_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\refany\array3_d\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\refany\array3_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\tailcall\deep_array_nz_d\*" /> @@ -1126,6 +1150,7 @@ <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical\VT\port\huge_gcref_r\*" /> <ExcludeList Include="$(XunitTestBinBase)/JIT/Performance/CodeQuality/Benchstones/MDBenchI/MDGeneralArray/MDGeneralArray/*" /> <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/regressions/GitHub_93597/GitHub_93597/*" /> + <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b59899/b59899/*" /> <!-- Covariant returns --> <!-- https://github.com/dotnet/runtimelab/issues/205 --> diff --git a/src/tests/readytorun/determinism/crossgen2determinism.csproj b/src/tests/readytorun/determinism/crossgen2determinism.csproj index f48f4be1d58a8..30f34184583c1 100644 --- a/src/tests/readytorun/determinism/crossgen2determinism.csproj +++ b/src/tests/readytorun/determinism/crossgen2determinism.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current"> +<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current" InitialTargets="PrepareDLLs"> <PropertyGroup> <!-- Needed for CLRTestTargetUnsupported, GCStressIncompatible, CrossGenTest --> <RequiresProcessIsolation>true</RequiresProcessIsolation> @@ -14,20 +14,26 @@ <ItemGroup> <Compile Include="Program.cs" /> </ItemGroup> + + <Target Name="PrepareDLLs"> + <MSBuild Projects="..\crossgen2\crossgen2smoke.csproj" Properties="OutputPath=$(OutputPath)\crossgen2smoke" /> + <Delete Files="$(OutputPath)\crossgen2smoke\crossgen2smoke.sh" /> + </Target> + <PropertyGroup> <CLRTestBatchPreCommands><![CDATA[ $(CLRTestBatchPreCommands) set CoreRT_DeterminismSeed=1 -%Core_Root%\CoreRun.exe %Core_Root%\crossgen2\crossgen2.dll --map -r:%Core_Root%\*.dll -r:..\..\crossgen2\crossgen2smoke\helperdll.dll -r:..\..\crossgen2\crossgen2smoke\helperildll.dll -o:crossgen2smoke1.ildll ..\..\crossgen2\crossgen2smoke\crossgen2smoke.dll +%Core_Root%\CoreRun.exe %Core_Root%\crossgen2\crossgen2.dll --map -r:%Core_Root%\*.dll -r:.\crossgen2smoke\helperdll.dll -r:.\crossgen2smoke\helperildll.dll -o:crossgen2smoke1.ildll .\crossgen2smoke\crossgen2smoke.dll set CoreRT_DeterminismSeed=2 -%Core_Root%\CoreRun.exe %Core_Root%\crossgen2\crossgen2.dll --map -r:%Core_Root%\*.dll -r:..\..\crossgen2\crossgen2smoke\helperdll.dll -r:..\..\crossgen2\crossgen2smoke\helperildll.dll -o:crossgen2smoke2.ildll ..\..\crossgen2\crossgen2smoke\crossgen2smoke.dll +%Core_Root%\CoreRun.exe %Core_Root%\crossgen2\crossgen2.dll --map -r:%Core_Root%\*.dll -r:.\crossgen2smoke\helperdll.dll -r:.\crossgen2smoke\helperildll.dll -o:crossgen2smoke2.ildll .\crossgen2smoke\crossgen2smoke.dll ]]></CLRTestBatchPreCommands> <CLRTestBashPreCommands><![CDATA[ $(CLRTestBashPreCommands) export CoreRT_DeterminismSeed=1 -$CORE_ROOT/corerun $CORE_ROOT/crossgen2/crossgen2.dll --map -r:$CORE_ROOT/*.dll -r:../../crossgen2/crossgen2smoke/helperdll.dll -r:../../crossgen2/crossgen2smoke/helperildll.dll -o:crossgen2smoke1.ildll ../../crossgen2/crossgen2smoke/crossgen2smoke.dll +$CORE_ROOT/corerun $CORE_ROOT/crossgen2/crossgen2.dll --map -r:$CORE_ROOT/*.dll -r:./crossgen2smoke/helperdll.dll -r:./crossgen2smoke/helperildll.dll -o:crossgen2smoke1.ildll ./crossgen2smoke/crossgen2smoke.dll export CoreRT_DeterminismSeed=2 -$CORE_ROOT/corerun $CORE_ROOT/crossgen2/crossgen2.dll --map -r:$CORE_ROOT/*.dll -r:../../crossgen2/crossgen2smoke/helperdll.dll -r:../../crossgen2/crossgen2smoke/helperildll.dll -o:crossgen2smoke2.ildll ../../crossgen2/crossgen2smoke/crossgen2smoke.dll +$CORE_ROOT/corerun $CORE_ROOT/crossgen2/crossgen2.dll --map -r:$CORE_ROOT/*.dll -r:./crossgen2smoke/helperdll.dll -r:./crossgen2smoke/helperildll.dll -o:crossgen2smoke2.ildll ./crossgen2smoke/crossgen2smoke.dll ]]></CLRTestBashPreCommands> </PropertyGroup> </Project> diff --git a/src/tests/reflection/Tier1Collectible/Tier1Collectible.csproj b/src/tests/reflection/Tier1Collectible/Tier1Collectible.csproj index 0b22e5805e2ff..61914665377d2 100644 --- a/src/tests/reflection/Tier1Collectible/Tier1Collectible.csproj +++ b/src/tests/reflection/Tier1Collectible/Tier1Collectible.csproj @@ -4,6 +4,8 @@ <RequiresProcessIsolation>true</RequiresProcessIsolation> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestPriority>1</CLRTestPriority> + <!-- Reflection.Emit --> + <NativeAotIncompatible>true</NativeAotIncompatible> </PropertyGroup> <ItemGroup> <Compile Include="$(MSBuildProjectName).cs" /> diff --git a/src/tests/run.py b/src/tests/run.py index 5f2a90c4468ab..7947fd552837c 100755 --- a/src/tests/run.py +++ b/src/tests/run.py @@ -1300,7 +1300,8 @@ def parse_test_results_xml_file(args, item, item_name, tests, assemblies): "result" : result, "time": time, "test_output": test_output, - "assembly_display_name": display_name + "assembly_display_name": display_name, + "is_merged": assembly_is_merged_tests_run })) if result == "Pass": assembly_info["passed"] += 1 @@ -1408,7 +1409,9 @@ def create_repro(args, env, tests): # Now that the args.repro_location exists under <runtime>/artifacts # create wrappers which will simply run the test with the correct environment for test in failed_tests: - if test["test_path"] is None: + if test["is_merged"]: + print("Skipping repro for merged test: %s (%s)" % (test["name"], test["assembly_display_name"])) + elif test["test_path"] is None: print("Failed to create repro for test: %s (%s)" % (test["name"], test["assembly_display_name"])) else: debug_env = DebugEnv(args, env, test) diff --git a/src/tools/illink/README.md b/src/tools/illink/README.md index 66eacd90dbb9c..af33c811debbb 100644 --- a/src/tools/illink/README.md +++ b/src/tools/illink/README.md @@ -5,9 +5,9 @@ This project hosts various tools and msbuild tasks which are used when trimming ## IL Trimmer The [IL Trimmer](src/linker/README.md) is the developer's tool that can be used to produce apps that contain only code and assembly dependencies which are necessary to run the app. It's fully integrated into -.NET SDKs via [ILLink.Tasks](src/ILLink.Tasks/README.md) build task and exposed via `dotnet publish` trimming [settings](https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained#trim-your-app---cli). +.NET SDKs via [ILLink.Tasks](src/ILLink.Tasks/README.md) build task and exposed via `dotnet publish` trimming [settings](https://learn.microsoft.com/dotnet/core/deploying/trim-self-contained#trim-your-app---cli). -The trimmer is always enabled for all size sensitive .NET workloads like Blazor WebAssembly, Xamarin or .NET mobile and can be manually enabled for other project types. The default apps trimming setting can be further customized by using a number of [msbuild properties](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options). +The trimmer is always enabled for all size sensitive .NET workloads like Blazor WebAssembly, Xamarin or .NET mobile and can be manually enabled for other project types. The default apps trimming setting can be further customized by using a number of [msbuild properties](https://learn.microsoft.com/dotnet/core/deploying/trimming-options). ## Dependencies Analyzer diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs index 06ed3c7c44bf4..49a44d28dae8f 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/COMAnalyzer.cs @@ -20,7 +20,7 @@ public sealed class COMAnalyzer : DiagnosticAnalyzer private const string MarshalAsAttribute = nameof (MarshalAsAttribute); static readonly DiagnosticDescriptor s_correctnessOfCOMCannotBeGuaranteed = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, - helpLinkUri: "https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-warnings/il2050"); + helpLinkUri: "https://learn.microsoft.com/dotnet/core/deploying/trim-warnings/il2050"); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (s_correctnessOfCOMCannotBeGuaranteed); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/FeatureContextLattice.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/FeatureContextLattice.cs index 319730629e90a..347f2a85afd62 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/FeatureContextLattice.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/FeatureContextLattice.cs @@ -52,6 +52,15 @@ public FeatureContext Union (FeatureContext other) { return new FeatureContext (ValueSet<string>.Union (EnabledFeatures, other.EnabledFeatures)); } + + public override string ToString () + { + if (EnabledFeatures.IsUnknown ()) + return "All"; + if (EnabledFeatures.IsEmpty ()) + return "None"; + return string.Join (", ", EnabledFeatures.GetKnownValues ()); + } } public readonly struct FeatureContextLattice : ILattice<FeatureContext> diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs index 1a8f3cdef3635..00410c9bc45bd 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowAnalysis.cs @@ -29,8 +29,8 @@ public abstract class LocalDataFlowAnalysis<TValue, TContext, TLattice, TContext > where TValue : struct, IEquatable<TValue> where TContext : struct, IEquatable<TContext> - where TLattice : ILattice<TValue>, new() - where TContextLattice : ILattice<TContext>, new() + where TLattice : ILattice<TValue> + where TContextLattice : ILattice<TContext> where TTransfer : LocalDataFlowVisitor<TValue, TContext, TLattice, TContextLattice, TConditionValue> where TConditionValue : struct, INegate<TConditionValue> { @@ -39,18 +39,25 @@ public abstract class LocalDataFlowAnalysis<TValue, TContext, TLattice, TContext readonly IOperation OperationBlock; static LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice> GetLatticeAndEntryValue( + TLattice lattice, + TContextLattice contextLattice, TContext initialContext, out LocalStateAndContext<TValue, TContext> entryValue) { - LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice> lattice = new (new (new TLattice ()), new TContextLattice ()); + LocalStateAndContextLattice<TValue, TContext, TLattice, TContextLattice> localStateAndContextLattice = new (new (lattice), contextLattice); entryValue = new LocalStateAndContext<TValue, TContext> (default (LocalState<TValue>), initialContext); - return lattice; + return localStateAndContextLattice; } // The initial value of the local dataflow is the empty local state (no tracked assignments), // with an initial context that must be specified by the derived class. - protected LocalDataFlowAnalysis (OperationBlockAnalysisContext context, IOperation operationBlock, TContext initialContext) - : base (GetLatticeAndEntryValue (initialContext, out var entryValue), entryValue) + protected LocalDataFlowAnalysis ( + OperationBlockAnalysisContext context, + IOperation operationBlock, + TLattice lattice, + TContextLattice contextLattice, + TContext initialContext) + : base (GetLatticeAndEntryValue (lattice, contextLattice, initialContext, out var entryValue), entryValue) { Context = context; OperationBlock = operationBlock; diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs index 8949b249b35ec..d3331d1137437 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs @@ -20,13 +20,13 @@ public sealed class RequiresAssemblyFilesAnalyzer : RequiresAnalyzerBase public const string RequiresAssemblyFilesAttributeFullyQualifiedName = "System.Diagnostics.CodeAnalysis." + RequiresAssemblyFilesAttribute; static readonly DiagnosticDescriptor s_locationRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.AvoidAssemblyLocationInSingleFile, - helpLinkUri: "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3000"); + helpLinkUri: "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3000"); static readonly DiagnosticDescriptor s_getFilesRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.AvoidAssemblyGetFilesInSingleFile, - helpLinkUri: "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001"); + helpLinkUri: "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001"); static readonly DiagnosticDescriptor s_requiresAssemblyFilesRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresAssemblyFiles, - helpLinkUri: "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3002"); + helpLinkUri: "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3002"); static readonly DiagnosticDescriptor s_requiresAssemblyFilesAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresAssemblyFilesAttributeMismatch); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimDataFlowAnalysis.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimDataFlowAnalysis.cs index 60cbe85649932..cb669462f354a 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimDataFlowAnalysis.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimDataFlowAnalysis.cs @@ -41,7 +41,12 @@ public TrimDataFlowAnalysis ( OperationBlockAnalysisContext context, DataFlowAnalyzerContext dataFlowAnalyzerContext, IOperation operationBlock) - : base (context, operationBlock, initialContext: FeatureContext.None) + : base ( + context, + operationBlock, + default (ValueSetLattice<SingleValue>), + new FeatureContextLattice (), + initialContext: FeatureContext.None) { TrimAnalysisPatterns = new TrimAnalysisPatternStore (lattice.LocalStateLattice.Lattice.ValueLattice, lattice.ContextLattice); _dataFlowAnalyzerContext = dataFlowAnalyzerContext; @@ -172,6 +177,18 @@ static void WriteIndented (string? s, int level) } } + public override void TraceEdgeInput ( + IControlFlowGraph<BlockProxy, RegionProxy>.ControlFlowBranch branch, + LocalStateValue state + ) { + if (trace && showStates) { + var source = branch.Source.Block.Ordinal; + var target = branch.Destination?.Block.Ordinal; + WriteIndented ($"--- Edge from [{source}] to [{target}] ---", 1); + WriteIndented (state.ToString (), 2); + } + } + public override void TraceBlockInput ( LocalStateValue normalState, LocalStateValue? exceptionState, diff --git a/src/tools/illink/src/ILLink.Shared/DataFlow/DefaultValueDictionary.cs b/src/tools/illink/src/ILLink.Shared/DataFlow/DefaultValueDictionary.cs index 49dc72f2b14a0..bc0501dc670bf 100644 --- a/src/tools/illink/src/ILLink.Shared/DataFlow/DefaultValueDictionary.cs +++ b/src/tools/illink/src/ILLink.Shared/DataFlow/DefaultValueDictionary.cs @@ -105,7 +105,7 @@ public DefaultValueDictionary<TKey, TValue> Clone () return new DefaultValueDictionary<TKey, TValue> (defaultValue, dict); } - // Prevent warning CS0659 https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0659. + // Prevent warning CS0659 https://learn.microsoft.com/dotnet/csharp/misc/cs0659. // This type should never be used as a dictionary key. public override int GetHashCode () => throw new NotImplementedException (); diff --git a/src/tools/illink/src/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs b/src/tools/illink/src/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs index 807f0c46b9b5f..181e81125d27f 100644 --- a/src/tools/illink/src/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs +++ b/src/tools/illink/src/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs @@ -163,6 +163,9 @@ public virtual void TraceStart (TControlFlowGraph cfg) { } [Conditional ("DEBUG")] public virtual void TraceVisitBlock (TBlock block) { } + [Conditional ("DEBUG")] + public virtual void TraceEdgeInput (IControlFlowGraph<TBlock, TRegion>.ControlFlowBranch branch, TValue state) { } + [Conditional ("DEBUG")] public virtual void TraceBlockInput (TValue normalState, TValue? exceptionState, TValue? exceptionFinallyState) { } @@ -280,7 +283,7 @@ public void Fixpoint (TControlFlowGraph cfg, TTransfer transfer) TValue predecessorState = cfgState.Get (predecessor).Current; FlowStateThroughExitedFinallys (predecessor, ref predecessorState); - + TraceEdgeInput (predecessor, predecessorState); currentState = lattice.Meet (currentState, predecessorState); } // State at start of a catch also includes the exceptional state from diff --git a/src/tools/illink/src/ILLink.Shared/DataFlow/IControlFlowGraph.cs b/src/tools/illink/src/ILLink.Shared/DataFlow/IControlFlowGraph.cs index d7b6a45b9ae0c..2fb81fb5e8ac0 100644 --- a/src/tools/illink/src/ILLink.Shared/DataFlow/IControlFlowGraph.cs +++ b/src/tools/illink/src/ILLink.Shared/DataFlow/IControlFlowGraph.cs @@ -45,7 +45,7 @@ public interface IControlFlowGraph<TBlock, TRegion> public readonly struct ControlFlowBranch : IEquatable<ControlFlowBranch> { public readonly TBlock Source; - private readonly TBlock? Destination; + public readonly TBlock? Destination; // The finally regions exited when control flows through this edge. // For example: diff --git a/src/tools/illink/src/ILLink.Shared/DataFlow/MaybeLattice.cs b/src/tools/illink/src/ILLink.Shared/DataFlow/MaybeLattice.cs index 65726b205539c..89a5d1f98d16e 100644 --- a/src/tools/illink/src/ILLink.Shared/DataFlow/MaybeLattice.cs +++ b/src/tools/illink/src/ILLink.Shared/DataFlow/MaybeLattice.cs @@ -9,7 +9,7 @@ namespace ILLink.Shared.DataFlow { // Wrapper for Nullable<T> which implements IEquatable so that this may // be used as a lattice value. Nullable types can't satisfy interface constraints; - // see for example https://docs.microsoft.com/dotnet/csharp/misc/cs0313. + // see for example https://learn.microsoft.com/dotnet/csharp/misc/cs0313. public struct Maybe<T> : IEquatable<Maybe<T>>, IDeepCopyValue<Maybe<T>> where T : struct, IEquatable<T> { diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index a1c6c7ff82976..08ab3bdda1659 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids --> +<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids --> <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Suppression> <DiagnosticId>CP0001</DiagnosticId> diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index d6cc0630a65d5..3d6a155fa17e9 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -343,19 +343,8 @@ internal void MarkEntireType (TypeDefinition type, in DependencyInfo reason, Mes MarkGenericParameterProvider (type, origin); - if (type.HasFields) { - foreach (FieldDefinition field in type.Fields) { - MarkFieldVisibleToReflection (field, new DependencyInfo (DependencyKind.MemberOfType, type), origin); - } - } - - if (type.HasMethods) { - foreach (MethodDefinition method in type.Methods) { - if (IsFullyPreservedAction (Annotations.GetAction (type.Module.Assembly))) - Annotations.SetAction (method, MethodAction.ForceParse); - MarkMethodVisibleToReflection (method, new DependencyInfo (DependencyKind.MemberOfType, type), origin); - } - } + MarkFieldsVisibleToReflection (type, new DependencyInfo (DependencyKind.MemberOfType, type), origin); + MarkMethodsVisibleToReflection (type, new DependencyInfo (DependencyKind.MemberOfType, type), origin); if (type.HasProperties) { foreach (var property in type.Properties) { @@ -1897,11 +1886,47 @@ internal void MarkMethodVisibleToReflection (MethodReference method, in Dependen } } + bool MarkMethodsVisibleToReflection (TypeDefinition type, in DependencyInfo reason, MessageOrigin origin) + { + if (!type.HasMethods) + return false; + + foreach (MethodDefinition method in type.Methods) { + if (IsFullyPreservedAction (Annotations.GetAction (type.Module.Assembly))) + Annotations.SetAction (method, MethodAction.ForceParse); + MarkMethodVisibleToReflection (method, reason, origin); + } + return true; + } + internal void MarkFieldVisibleToReflection (FieldReference field, in DependencyInfo reason, in MessageOrigin origin) { MarkField (field, reason, origin); } + bool MarkFieldsVisibleToReflection (TypeDefinition type, in DependencyInfo reason, MessageOrigin origin, bool markBackingFieldsOnlyIfPropertyMarked = false) + { + if (!type.HasFields) + return false; + + foreach (FieldDefinition field in type.Fields) { + if (markBackingFieldsOnlyIfPropertyMarked && field.Name.EndsWith (">k__BackingField", StringComparison.Ordinal)) { + // We can't reliably construct the expected property name from the backing field name for all compilers + // because csc shortens the name of the backing field in some cases + // For example: + // Field Name = <IFoo<int>.Bar>k__BackingField + // Property Name = IFoo<System.Int32>.Bar + // + // instead we will search the properties and find the one that makes use of the current backing field + var propertyDefinition = SearchPropertiesForMatchingFieldDefinition (field); + if (propertyDefinition != null && !Annotations.IsMarked (propertyDefinition)) + continue; + } + MarkFieldVisibleToReflection (field, reason, origin); + } + return true; + } + internal void MarkPropertyVisibleToReflection (PropertyDefinition property, in DependencyInfo reason, in MessageOrigin origin) { // Marking the property itself actually doesn't keep it (it only marks its attributes and records the dependency), we have to mark the methods on it @@ -2257,24 +2282,19 @@ void MarkTypeWithDebuggerDisplayAttributeValue (TypeDefinition type, CustomAttri MethodDefinition? method = GetMethodWithNoParameters (type, methodName); if (method != null) { - MarkMethod (method, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); + MarkMethodVisibleToReflection (method, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); continue; } } else { FieldDefinition? field = GetField (type, realMatch); if (field != null) { - MarkField (field, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); + MarkFieldVisibleToReflection (field, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); continue; } PropertyDefinition? property = GetProperty (type, realMatch); if (property != null) { - if (property.GetMethod != null) { - MarkMethod (property.GetMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); - } - if (property.SetMethod != null) { - MarkMethod (property.SetMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); - } + MarkPropertyVisibleToReflection (property, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); continue; } } @@ -2282,8 +2302,8 @@ void MarkTypeWithDebuggerDisplayAttributeValue (TypeDefinition type, CustomAttri while (true) { // Currently if we don't understand the DebuggerDisplayAttribute we mark everything on the type // This can be improved: dotnet/linker/issues/1873 - MarkMethods (type, new DependencyInfo (DependencyKind.KeptForSpecialAttribute, attribute), origin); - MarkFields (type, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); + MarkMethodsVisibleToReflection (type, new DependencyInfo (DependencyKind.KeptForSpecialAttribute, attribute), origin); + MarkFieldsVisibleToReflection (type, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); if (Context.TryResolve (type.BaseType) is not TypeDefinition baseType) break; type = baseType; @@ -2311,8 +2331,8 @@ void MarkTypeWithDebuggerTypeProxyAttribute (TypeDefinition type, CustomAttribut MarkType (proxyTypeReference, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); if (Context.TryResolve (proxyTypeReference) is TypeDefinition proxyType) { - MarkMethods (proxyType, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); - MarkFields (proxyType, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); + MarkMethodsVisibleToReflection (proxyType, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); + MarkFieldsVisibleToReflection (proxyType, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), origin); } } } @@ -2781,16 +2801,16 @@ void ApplyPreserveInfo (TypeDefinition type) switch (preserve) { case TypePreserve.All: - MarkFields (type, true, di, typeOrigin); - MarkMethods (type, di, typeOrigin); + MarkFieldsVisibleToReflection (type, di, typeOrigin); + MarkMethodsVisibleToReflection (type, in di, typeOrigin); return; case TypePreserve.Fields: - if (!MarkFields (type, true, di, typeOrigin, true)) + if (!MarkFieldsVisibleToReflection (type, di, typeOrigin, markBackingFieldsOnlyIfPropertyMarked: true)) Context.LogWarning (type, DiagnosticId.TypeHasNoFieldsToPreserve, type.GetDisplayName ()); break; case TypePreserve.Methods: - if (!MarkMethods (type, di, typeOrigin)) + if (!MarkMethodsVisibleToReflection (type, in di, typeOrigin)) Context.LogWarning (type, DiagnosticId.TypeHasNoMethodsToPreserve, type.GetDisplayName ()); break; } @@ -2802,18 +2822,18 @@ void ApplyPreserveInfo (TypeDefinition type) if (type.HasMethods) { foreach (var m in type.Methods) { if ((members & TypePreserveMembers.Visible) != 0 && IsMethodVisible (m)) { - MarkMethod (m, di, typeOrigin); + MarkMethodVisibleToReflection (m, di, typeOrigin); continue; } if ((members & TypePreserveMembers.Internal) != 0 && IsMethodInternal (m)) { - MarkMethod (m, di, typeOrigin); + MarkMethodVisibleToReflection (m, di, typeOrigin); continue; } if ((members & TypePreserveMembers.Library) != 0) { if (IsSpecialSerializationConstructor (m) || HasOnSerializeOrDeserializeAttribute (m)) { - MarkMethod (m, di, typeOrigin); + MarkMethodVisibleToReflection (m, di, typeOrigin); continue; } } @@ -2823,12 +2843,12 @@ void ApplyPreserveInfo (TypeDefinition type) if (type.HasFields) { foreach (var f in type.Fields) { if ((members & TypePreserveMembers.Visible) != 0 && IsFieldVisible (f)) { - MarkField (f, di, typeOrigin); + MarkFieldVisibleToReflection (f, di, typeOrigin); continue; } if ((members & TypePreserveMembers.Internal) != 0 && IsFieldInternal (f)) { - MarkField (f, di, typeOrigin); + MarkFieldVisibleToReflection (f, di, typeOrigin); continue; } } @@ -2876,7 +2896,7 @@ void ApplyPreserveMethods (MethodDefinition method, MessageOrigin origin) MarkMethodCollection (list, new DependencyInfo (DependencyKind.PreservedMethod, method), origin); } - protected bool MarkFields (TypeDefinition type, bool includeStatic, in DependencyInfo reason, MessageOrigin origin, bool markBackingFieldsOnlyIfPropertyMarked = false) + protected bool MarkFields (TypeDefinition type, bool includeStatic, in DependencyInfo reason, MessageOrigin origin) { if (!type.HasFields) return false; @@ -2884,19 +2904,6 @@ protected bool MarkFields (TypeDefinition type, bool includeStatic, in Dependenc foreach (FieldDefinition field in type.Fields) { if (!includeStatic && field.IsStatic) continue; - - if (markBackingFieldsOnlyIfPropertyMarked && field.Name.EndsWith (">k__BackingField", StringComparison.Ordinal)) { - // We can't reliably construct the expected property name from the backing field name for all compilers - // because csc shortens the name of the backing field in some cases - // For example: - // Field Name = <IFoo<int>.Bar>k__BackingField - // Property Name = IFoo<System.Int32>.Bar - // - // instead we will search the properties and find the one that makes use of the current backing field - var propertyDefinition = SearchPropertiesForMatchingFieldDefinition (field); - if (propertyDefinition != null && !Annotations.IsMarked (propertyDefinition)) - continue; - } MarkField (field, reason, origin); } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index c59afbfd8fd5f..167794b4fee4b 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -118,6 +118,12 @@ public Task ConstructorDataFlow () return RunTest (); } + [Fact] + public Task DataflowInLocalMethodGroupArgument () + { + return RunTest (); + } + [Fact] public Task DynamicDependencyDataflow () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs index b711d35078c66..3f722ad634e3d 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs @@ -82,6 +82,12 @@ public Task RequiresOnClass () return RunTest (nameof (RequiresOnClass)); } + [Fact] + public Task RequiresOnEvents () + { + return RunTest (); + } + [Fact] public Task RequiresOnStaticConstructor () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/AssemblyAttributeAccessesMembers.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/AssemblyAttributeAccessesMembers.cs new file mode 100644 index 0000000000000..5d1ebb39cfb18 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/AssemblyAttributeAccessesMembers.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Attributes; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +[assembly: KeptAttributeAttribute (typeof (AccessesMembersAttribute))] +[assembly: AccessesMembers (typeof (AssemblyAttributeAccessesMembers.TypeWithMembers))] +namespace Mono.Linker.Tests.Cases.Attributes +{ + /// <summary> + /// A regression test for the issue that was fixed by https://github.com/dotnet/runtime/pull/102613. + /// Events were assumed to always have a MemberDefinition or Descriptor file as the warning origin, but it also can occur from an assembly attribute. + /// </summary> + [Kept] + class AssemblyAttributeAccessesMembers + { + [Kept] + public static void Main () + { + typeof (AssemblyAttributeAccessesMembers).Assembly.GetCustomAttributes (false); + } + + [Kept] + public class TypeWithMembers + { + [Kept] + public TypeWithMembers () { } + + [Kept] + public void Method () { } + + [Kept] + public int Field; + + [Kept] + [KeptBackingField] + public int Property { [Kept] get; [Kept] set; } + + [Kept] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + [KeptBackingField] + public event EventHandler Event; + } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + public class AccessesMembersAttribute : Attribute + { + [Kept] + public AccessesMembersAttribute ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DataflowInLocalMethodGroupArgument.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DataflowInLocalMethodGroupArgument.cs new file mode 100644 index 0000000000000..3ecffa38f953e --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DataflowInLocalMethodGroupArgument.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DataflowInLocalMethodGroupArgument + { + public static void Main () + { + new GenericWithNonPublicConstructors<int> ().Test (); + GenericWithNonPublicConstructors<int>.TestInstanceLocalMethod (); + } + + public class GenericWithNonPublicConstructors<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] T> + { + public void Test () + { + [ExpectedWarning ("IL2087", "PublicFields", "RequiresPublicFields", "T", "GenericWithNonPublicConstructors")] + static int GetIntWithBadDataflow (int x) + { + typeof (T).RequiresPublicFields(); + return 0; + } + + new Dictionary<int, int> ().GetOrAdd (0, GetIntWithBadDataflow); + } + + public static void TestInstanceLocalMethod () + { + [ExpectedWarning ("IL2087", "PublicFields", "RequiresPublicFields", "T", "GenericWithNonPublicConstructors")] + int GetIntWithBadDataflow (int x) + { + typeof (T).RequiresPublicFields(); + return 0; + } + + new Dictionary<int, int> ().GetOrAdd (0, GetIntWithBadDataflow); + } + } + } + + public static class DictionaryExtensions + { + public static TValue GetOrAdd<TKey, TValue> (this Dictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) + { + if (dictionary.TryGetValue (key, out var value)) + return value; + + value = valueFactory (key); + dictionary.TryAdd (key, value); + return value; + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs index d98b7dc2aa57e..d334b99439d96 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs @@ -154,7 +154,7 @@ static void MethodWithRequires () } [ExpectedWarning ("IL2026", nameof (MethodWithRequires))] - [ExpectedWarning ("IL3050", nameof (MethodWithRequires), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (MethodWithRequires), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] public static void Test () { MethodWithRequires (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs index a7715c507f2a3..ae3ffecd1aea9 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs @@ -94,7 +94,7 @@ class GenericTypeWithRequires< { } - [ExpectedWarning ("IL3050", Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] // The way we track arrays causes the analyzer to track exponentially many // ArrayValues in the ValueSet for the pattern in this method, hitting the limit. // When this happens, we replace the ValueSet with an unknown value, producing diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index ab94f5052d429..bff8cbfe57acc 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -40,8 +40,8 @@ public static void Main () class CallFeatureUnguarded { [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void Unguarded () { RequiresUnreferencedCode (); @@ -50,8 +50,8 @@ static void Unguarded () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void UnguardedIf () { if (!TestFeatures.IsUnreferencedCodeSupported) { @@ -62,8 +62,8 @@ static void UnguardedIf () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void UnguardedElse () { if (TestFeatures.IsUnreferencedCodeSupported) @@ -99,8 +99,8 @@ static void UnguardedTernary () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void UnguardedThrow () { if (TestFeatures.IsUnreferencedCodeSupported) @@ -114,8 +114,8 @@ static void UnguardedThrow () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void UnguardedReturn () { if (TestFeatures.IsUnreferencedCodeSupported) @@ -205,8 +205,8 @@ public static void Test () GuardedDoesNotReturnIfFalseCtor (); } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] static void GuardedIf () { if (TestFeatures.IsUnreferencedCodeSupported) { @@ -216,8 +216,8 @@ static void GuardedIf () } } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] static void GuardedElse () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -248,8 +248,8 @@ static void GuardedTernary () var b = !TestFeatures.IsUnreferencedCodeSupported ? true : RequiresUnreferencedCodeBool (); } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] static void GuardedThrow () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -262,8 +262,8 @@ static void GuardedThrow () RequiresAssemblyFiles (); } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")] static void GuardedReturn () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -277,7 +277,7 @@ static void GuardedReturn () } // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void GuardedAssert () { Debug.Assert (TestFeatures.IsUnreferencedCodeSupported); @@ -286,7 +286,7 @@ static void GuardedAssert () } // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void GuardedDoesNotReturnIfTrue () { DoesNotReturnIfTrue (!TestFeatures.IsUnreferencedCodeSupported); @@ -295,7 +295,7 @@ static void GuardedDoesNotReturnIfTrue () } // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void GuardedDoesNotReturnIfFalse () { DoesNotReturnIfFalse (TestFeatures.IsUnreferencedCodeSupported); @@ -304,7 +304,7 @@ static void GuardedDoesNotReturnIfFalse () } // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void GuardedDoesNotReturn () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -314,7 +314,7 @@ static void GuardedDoesNotReturn () } // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void GuardedDoesNotReturnIfFalseCtor () { new DoesNotReturnIfFalseCtor (TestFeatures.IsUnreferencedCodeSupported); @@ -326,8 +326,8 @@ static void GuardedDoesNotReturnIfFalseCtor () class FeatureCheckBooleanExpressions { // Trimmer/NativeAot aren't able to optimize away the branch in this case. - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "")] + [UnexpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] + [UnexpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] static void And () { if (TestFeatures.IsUnreferencedCodeSupported && RuntimeFeature.IsDynamicCodeSupported) { @@ -337,7 +337,7 @@ static void And () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void AndNot () { if (!TestFeatures.IsUnreferencedCodeSupported && !RuntimeFeature.IsDynamicCodeSupported) @@ -348,8 +348,8 @@ static void AndNot () } // Trimmer/NativeAot aren't able to optimize away the branch in this case. - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "")] + [UnexpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] + [UnexpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] static void NotAnd () { if (!(TestFeatures.IsUnreferencedCodeSupported && RuntimeFeature.IsDynamicCodeSupported)) @@ -360,7 +360,7 @@ static void NotAnd () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void Or () { if (TestFeatures.IsUnreferencedCodeSupported || RuntimeFeature.IsDynamicCodeSupported) { @@ -370,8 +370,8 @@ static void Or () } // Trimmer/NativeAot aren't able to optimize away the branch in this case. - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "")] + [UnexpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] + [UnexpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] static void OrNot () { if (!TestFeatures.IsUnreferencedCodeSupported || !RuntimeFeature.IsDynamicCodeSupported) @@ -382,7 +382,7 @@ static void OrNot () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void NotOr () { if (!(TestFeatures.IsUnreferencedCodeSupported || RuntimeFeature.IsDynamicCodeSupported)) @@ -424,7 +424,7 @@ static void NotEqualsTrue () { if (TestFeatures.IsUnreferencedCodeSupported != true) throw null; - + RequiresUnreferencedCode (); } @@ -477,7 +477,7 @@ static void IsNotFalse () } [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode))] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/102830")] static void Contradiction () { if (TestFeatures.IsUnreferencedCodeSupported && !TestFeatures.IsUnreferencedCodeSupported) { @@ -532,7 +532,7 @@ static void CallTestDynamicCodeGuarded () RequiresDynamicCode (); } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void CallTestDynamicCodeUnguarded () { RequiresDynamicCode (); @@ -545,7 +545,7 @@ static void CallTestAssemblyFilesGuarded () } } - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")] static void CallTestAssemblyFilesUnguarded () { RequiresAssemblyFiles (); @@ -564,9 +564,9 @@ public static void Test () class FeatureCheckCombinations { - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Trimmer won't warn for NativeAOT specific warning. Both features are disabled in NativeAOT and no path can reach the problematic methods")] // Trimmer warns because IsDynamicCodeSupported is not a constant, so the call is reachable. - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Analyzer | Tool.Trimmer, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Analyzer | Tool.Trimmer, "Both features are disabled in NativeAOT and no path can reach the problematic methods")] static void MeetFeaturesEmptyIntersection (bool b = true) { if (b) { @@ -582,7 +582,7 @@ static void MeetFeaturesEmptyIntersection (bool b = true) // Shows that ILLink has the same branch removal as NativeAot for this pattern, when // the branches both use a feature check that's substituted by ILLink. - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Analyzer and NativeAOT eliminate the branch, but Analyzer can only guarantee UnreferencedCode is available")] static void MeetFeaturesEmptyIntersection_IdenticalBranches (bool b = true) { if (b) { @@ -596,7 +596,7 @@ static void MeetFeaturesEmptyIntersection_IdenticalBranches (bool b = true) RequiresDynamicCode (); } - [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "")] + [ExpectedWarning ("IL3050", nameof (RequiresDynamicCode), Tool.Analyzer, "Analyzer and NativeAOT eliminate the branch, but Analyzer can only guarantee UnreferencedCode is available")] static void MeetFeaturesIntersection (bool b = true) { if (b) { @@ -622,7 +622,7 @@ static void IntroduceFeature () } } - [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "")] + [ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Analyzer and NativeAOT eliminate the branch, but Analyzer can only guarantee UnreferencedCode is available")] static void RemoveFeature () { if (TestFeatures.IsUnreferencedCodeSupported) { @@ -787,7 +787,7 @@ static void NestedTryInCheckInFinally () [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInTryNoCatch () { try { Debug.Assert (TestFeatures.IsUnreferencedCodeSupported); @@ -828,7 +828,7 @@ static void AssertInCatch () { [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInFinally () { try { RequiresUnreferencedCode0 (); @@ -843,8 +843,8 @@ static void AssertInFinally () { [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInTryNestedInTry () { try { @@ -885,8 +885,8 @@ static void AssertInTryWithCatchNestedInTry () [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInTryNestedInFinally () { try { @@ -925,8 +925,8 @@ static void AssertInTryWithCatchNestedInFinally () [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInFinallyNestedInTry () { try { try { @@ -944,8 +944,8 @@ static void AssertInFinallyNestedInTry () { [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInFinallyWithCatchNestedInTry () { try { try { @@ -965,8 +965,8 @@ static void AssertInFinallyWithCatchNestedInTry () { [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))] // Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInFinallyNestedInFinally () { try { @@ -985,8 +985,8 @@ static void AssertInFinallyNestedInFinally () [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))] [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))] // Trimmer/NativeAot doesn't optimize branches away based on DoesNotReturnIfAttribute - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "")] - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")] static void AssertInFinallyWithCatchNestedInFinally () { try { @@ -1056,7 +1056,7 @@ static IEnumerable<int> GuardInIterator () } } - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer, "", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer, "https://github.com/dotnet/linker/issues/3087", CompilerGeneratedCode = true)] static IEnumerable<int> StateFlowsAcrossYield () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -1075,7 +1075,7 @@ static async Task GuardInAsync () } } - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/linker/issues/3087", CompilerGeneratedCode = true)] static async Task StateFlowsAcrossAwait () { if (!TestFeatures.IsUnreferencedCodeSupported) @@ -1095,7 +1095,7 @@ static async IAsyncEnumerable<int> GuardInAsyncIterator () } } - [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/linker/issues/3087", CompilerGeneratedCode = true)] static async IAsyncEnumerable<int> StateFlowsAcrossAwaitAndYield () { if (!TestFeatures.IsUnreferencedCodeSupported) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/DebuggerDisplayNamesAreKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/DebuggerDisplayNamesAreKept.cs new file mode 100644 index 0000000000000..7dc78cfdf0107 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/DebuggerDisplayNamesAreKept.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [VerifyMetadataNames] + public class DebuggerDisplayNamesAreKept + { + public static void Main () + { + var t = new TypeWithDebuggerDisplayAttribute (); + var u = new TypeWithDebuggerTypeProxyAttribute (); + } + + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DebuggerDisplayAttribute))] + [DebuggerDisplay ("{MemberNotFound}")] + class TypeWithDebuggerDisplayAttribute + { + [Kept] + public void MethodWithKeptParameterName (int arg) + { + } + } + + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DebuggerTypeProxyAttribute))] + [DebuggerTypeProxy (typeof (DebuggerTypeProxy))] + class TypeWithDebuggerTypeProxyAttribute + { + } + + [KeptMember (".ctor()")] + class DebuggerTypeProxy + { + [Kept] + public void MethodWithKeptParameterName (int arg) + { + } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootAllAssemblyNamesAreKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootAllAssemblyNamesAreKept.cs new file mode 100644 index 0000000000000..f251dfe17935d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootAllAssemblyNamesAreKept.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [VerifyMetadataNames] + [SetupLinkerArgument ("-a", "test.exe", "all")] + [KeptMember (".ctor()")] + public class RootAllAssemblyNamesAreKept + { + public static void Main () + { + } + + [Kept] + public void InstanceMethodWithKeptParameterName (int arg) + { + } + + [Kept] + public static void MethodWithKeptParameterName (string str) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.cs new file mode 100644 index 0000000000000..5ea1a34ae5eaa --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.cs @@ -0,0 +1,42 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [VerifyMetadataNames] + [SetupLinkerDescriptorFile ("RootDescriptorNamesAreKept.xml")] + public class RootDescriptorNamesAreKept + { + public static void Main () + { + } + + [KeptMember (".ctor()")] + class RootedType + { + [Kept] + void InstanceMethodWithKeptParameterName (int arg) + { + } + + [Kept] + static void MethodWithKeptParameterName (string str) + { + } + } + + [KeptMember (".ctor()")] + class TypeWithPreserveMethods + { + [Kept] + void InstanceMethodWithKeptParameterName (int arg) + { + } + + [Kept] + static void MethodWithKeptParameterName (string str) + { + } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.xml b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.xml new file mode 100644 index 0000000000000..ce7605d9e51c1 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootDescriptorNamesAreKept.xml @@ -0,0 +1,6 @@ +<linker> + <assembly fullname="test"> + <type fullname="Mono.Linker.Tests.Cases.Metadata.RootDescriptorNamesAreKept/RootedType" /> + <type fullname="Mono.Linker.Tests.Cases.Metadata.RootDescriptorNamesAreKept/TypeWithPreserveMethods" preserve="methods" /> + </assembly> +</linker> diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootLibraryAssemblyNamesAreKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootLibraryAssemblyNamesAreKept.cs new file mode 100644 index 0000000000000..e82ecf2e82800 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootLibraryAssemblyNamesAreKept.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [VerifyMetadataNames] + [SetupLinkerArgument ("-a", "test.exe", "library")] + [KeptMember (".ctor()")] + public class RootLibraryAssemblyNamesAreKept + { + public static void Main () + { + } + + [Kept] + public void InstanceMethodWithKeptParameterName (int arg) + { + } + + [Kept] + public static void MethodWithKeptParameterName (string str) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootVisibleAssemblyNamesAreKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootVisibleAssemblyNamesAreKept.cs new file mode 100644 index 0000000000000..bf0e09be13944 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Metadata/RootVisibleAssemblyNamesAreKept.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Metadata +{ + [VerifyMetadataNames] + [SetupLinkerArgument ("-a", "test.exe", "visible")] + [KeptMember (".ctor()")] + public class RootVisibleAssemblyNamesAreKept + { + public static void Main () + { + } + + [Kept] + public void InstanceMethodWithKeptParameterName (int arg) + { + } + + [Kept] + public static void MethodWithKeptParameterName (string str) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnEvents.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnEvents.cs new file mode 100644 index 0000000000000..bccca851227ee --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnEvents.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.RequiresCapability +{ + [ExpectedNoWarnings] + public class RequiresOnEvents + { + public static void Main () + { + Test (); + } + + [Kept] + static event EventHandler EventToTestAdd { + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [KeptAttributeAttribute (typeof (RequiresAssemblyFilesAttribute))] + [KeptAttributeAttribute (typeof (RequiresDynamicCodeAttribute))] + [RequiresUnreferencedCode ("Message for --EventToTestAdd.add--")] + [RequiresAssemblyFiles ("Message for --EventToTestAdd.add--")] + [RequiresDynamicCode ("Message for --EventToTestAdd.add--")] + [Kept (By = Tool.Trimmer)] + add { } + [Kept] + remove { } + } + + [Kept] + public static void Test () + { + // Marking remove marks the event and all its methods in the trimmer + // This should warn for the add method even if it's not explicitly used + // However, for NativeAOT, add is removed, so it shouldn't warn on NativeAOT + EventToTestAdd -= (sender, e) => { }; + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests/Extensions/NiceIO.cs b/src/tools/illink/test/Mono.Linker.Tests/Extensions/NiceIO.cs index 6969069dc9049..21871a9770f61 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/Extensions/NiceIO.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/Extensions/NiceIO.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // The MIT License(MIT) @@ -31,13 +31,15 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace Mono.Linker.Tests.Extensions { public class NPath : IEquatable<NPath>, IComparable { - private static readonly StringComparison PathStringComparison = IsLinux () ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + private static readonly StringComparison PathStringComparison = RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ? + StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; private readonly string[] _elements; private readonly bool _isRelative; @@ -804,11 +806,6 @@ static bool AlwaysTrue (NPath p) { return true; } - - private static bool IsLinux () - { - return Directory.Exists ("/proc"); - } } public static class Extensions