diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 76d5359f980ba1..da9f3bf10c05b0 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
- "version": "1.0.0-prerelease.22305.1",
+ "version": "1.0.0-prerelease.22320.3",
"commands": [
"xharness"
]
diff --git a/Directory.Build.props b/Directory.Build.props
index 45dabffc314c74..4f9312803f0309 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -35,6 +35,7 @@
arm64
loongarch64
s390x
+ ppc64le
wasm
x64
x64
diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT
index 307da15465cec2..83f7a5ab45da1a 100644
--- a/THIRD-PARTY-NOTICES.TXT
+++ b/THIRD-PARTY-NOTICES.TXT
@@ -1099,3 +1099,40 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License notice for code from The Practice of Programming
+-------------------------------
+
+Copyright (C) 1999 Lucent Technologies
+
+Excerpted from 'The Practice of Programming
+by Brian W. Kernighan and Rob Pike
+
+You may use this code for any purpose, as long as you leave the copyright notice and book citation attached.
+
+License notice for amd/aocl-libm-ose
+-------------------------------
+
+Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/docs/coding-guidelines/clr-code-guide.md b/docs/coding-guidelines/clr-code-guide.md
index 85987de5b047ed..88a4c3a06c74f7 100644
--- a/docs/coding-guidelines/clr-code-guide.md
+++ b/docs/coding-guidelines/clr-code-guide.md
@@ -437,7 +437,7 @@ A GC_NOTRIGGER function cannot:
[1] With one exception: GCX_COOP (which effects a preemp->coop->preemp roundtrip) is permitted. The rationale is that GCX_COOP becomes a NOP if the thread was cooperative to begin with so it's safe to allow this (and necessary to avoid some awkward code in our product.)
-**Note that for GC to be truly prevented, the caller must also ensure that the thread is in cooperative mode.** Otherwise, all the precautions above are in vain since any other thread can start a GC at any time. Given that, you might be wondering why cooperative mode is not part of the definition of GC_NOTRIGGER. In fact, there is a third thread state called GC_FORBID which is exactly that: GC_TRIGGERS plus forced cooperative mode. As its name implies, GC_FORBID _guarantees_ that no GC will occur on any thread.
+**Note that for GC to be truly prevented, the caller must also ensure that the thread is in cooperative mode.** Otherwise, all the precautions above are in vain since any other thread can start a GC at any time. Given that, you might be wondering why cooperative mode is not part of the definition of GC_NOTRIGGER. In fact, there is a third thread state called GC_FORBID which is exactly that: GC_NOTRIGGER plus forced cooperative mode. As its name implies, GC_FORBID _guarantees_ that no GC will occur on any thread.
Why do we use GC_NOTRIGGERS rather than GC_FORBID? Because forcing every function to choose between GC_TRIGGERS and GC_FORBID is too inflexible given that some callers don't actually care about GC. Consider a simple class member function that returns the value of a field. How should it be declared? If you choose GC_TRIGGERS, then the function cannot be legally called from a GC_NOTRIGGER function even though this is perfectly safe. If you choose GC_FORBID, then every caller must switch to cooperative mode to invoke the function just to prevent an assert. Thus, GC_NOTRIGGER was created as a middle ground and has become far more pervasive and useful than GC_FORBID. Callers who actually need GC stopped will have put themselves in cooperative mode anyway and in those cases, GC_NOTRIGGER actually becomes GC_FORBID. Callers who don't care can just call the function and not worry about modes.
diff --git a/docs/coding-guidelines/mono-code-guide.md b/docs/coding-guidelines/mono-code-guide.md
new file mode 100644
index 00000000000000..8433df9f7b6554
--- /dev/null
+++ b/docs/coding-guidelines/mono-code-guide.md
@@ -0,0 +1,253 @@
+# Mono code guidelines
+
+This document is meant to capture guidelines for contributing code to
+the [src/mono/mono/](../../src/mono/mono),
+[src/native/public/mono](../../src/native/public/mono) areas of the
+dotnet/runtime repo.
+
+In general this guide does not apply to:
+
+1. Shared native code in [src/native](../../src/native)
+2. The Mono-specific C# code in [src/mono](../../src/mono) in System.Private.CoreLib or elsewhere
+
+## Code style
+
+Mono is written in C.
+
+We follow the [Mono Coding guidelines](https://www.mono-project.com/community/contributing/coding-guidelines/) for the C code - in particular:
+
+* tabs, not spaces
+* space between a function name and the open parenthesis
+* braces on the same line as `if`, `for`, `while` etc
+
+## Naming
+
+Mono reserves the following prefixes for symbols: `mono_`, `monovm_`, `m_`, `monoeg_` (used to remap [`eglib`](../../src/mono/mono/eglib)
+functions which in source code have a `g_` prefix).
+
+All non-`static` symbols should use one of these prefixes. We generally use `mono_` for most
+functions. `m_` is used for some inline accessor functions and macros. `g_` (`mono_eg_`) is used
+for `eglib` functions.
+
+Types in a single C file can use any name. Types in a header should use a `Mono` (or sometimes
+`mono_`) prefix.
+
+Public API symbols and types *must* be prefixed.
+
+For types Mono generally uses `typedef struct _MonoWhatever { ... } MonoWhatever`. Opaque types may
+define `typedef struct _MonoWhatever MonoWhatever` in a client header and define `struct
+_MonoWhatever {...}` in an implementation header. Occasionally we break `#include` cycles by adding
+forward declarations for some types.
+
+## Macros
+
+Mono derives from an autotools-style project so we generally write `HOST_XYZ` for the machine on
+which the runtime is executing (as opposed to the machine on which the runtime was compiled), and
+`TARGET_XYZ` for the machine that will be targeted by the JIT and AOT compilers. In the case of AOT
+compilation host and target might be different: the host might be Windows, and the target might be
+Browser WebAssembly, for example.
+
+Macros generally use a `MONO_` prefix. Macros in public API headers *must* use the prefix.
+
+## Types
+
+Prefer the standard C sized types `int32_t`, `intptr_t`, etc over the eglib types `gint32`, `gsize` etc.
+
+One exception is `gboolean` is prefered over C `bool`.
+
+There are actually three boolean types to keep in mind:
+
+* `gboolean` used internally in the runtime
+* `MonoBoolean` used as an interop type with C# bool in internal calls
+* `mono_bool` used by the public C API - generally new code shouldn't use it except when adding a new public API function.
+
+## Utility and platform abstraction functions
+
+Mono generally tries to fill in POSIX-like abstractions on platforms that lack them (for example, Windows).
+This is in contrast to the CoreCLR PAL which generally tries to add Windows API abstractions on top
+of POSIX.
+
+If an existing `eglib` utility function is available, it should be used. New ones can be added.
+
+Other platform specific code is in `src/mono/utils`
+
+## Directory dependencies
+
+For the code in `src/mono/mono`:
+
+* `eglib` should not depend on other code from `src/mono`
+
+* `utils` can depend on `eglib` and 3rd party code from `src/native/external`
+
+* `sgen` depends on `eglib` and `utils`
+
+* `metadata` depends on all of the above (but note that some Boehm-GC files in this directory should not depend on `sgen`)
+
+* `mini` depends on all of the above
+
+* `mini/interp` depends on all of the above
+
+* `components` can use functions from all of the above provided they're marked with
+ `MONO_COMPONENT_API`, see [../design/mono/components.md](../design/mono/components.md)
+
+The main distinction between `metadata` and `utils` is that utils code should not assume that it is
+part of an executing .NET runtime - anything that loads types, creates objects, etc does not belong
+in utils but in metadata.
+
+The `mini` directory contains execution engines. If the execution engine has to provide some
+functionality to `metadata` it generally does so by installing some callback that is invoked by
+`metadata`. For example, `mini` knows how to unwind exceptions and perform stack walks - while
+`metadata` decides *when* to unwind or perform a stackwalk. To coordinate, `metadata` exposes an
+API to install hooks and `mini` provides the implementations.
+
+## Error handling
+
+New code should prefer to use the `MonoError` functions.
+
+* A non-public-API function should take a `MonoError *` argument.
+* In case of an error the function should call one of the `mono_error_set_` functions from [../../src/mono/mono/utils/mono-error-internals.h](../../src/mono/mono/utils/mono-error-internals.h)
+* Inside the runtime check whether there was an error by calling `is_ok (error)`, `mono_error_assert_ok (error)`, `goto_if_nok` or `return_if_nok`/`return_val_if_nok`
+* If there is an error and you're handling it, call `mono_error_cleanup (error)` to dispose of the resources.
+* `MonoError*` is generally one-shot: after it's been cleaned up it needs to be re-inited with `mono_error_init_reuse`, but this is discouraged.
+* Instead if you intend to deal with an error, use `ERROR_DECL (local_error)` then call the
+ possibly-failing function and pass it `local_error`, then call `mono_error_assert_ok
+ (local_error)` if it "can't fail" or `mono_error_cleanup (local_error)` if you want to ignore the
+ failure.
+
+## Managed Exceptions
+
+New code should generally not deal with `MonoException*`, use `MonoError*` instead.
+
+New code should avoid using `mono_error_set_pending_exception` - it affects a thread-local flag in
+a way that is not obvious to the caller of your function and may trample existing in-flight
+exceptions and make your code fail in unexpected ways.
+
+There are two circumstances when you might need to call `mono_error_set_pending_exception`:
+
+1. You're working with a public Mono API that sets a pending exception
+2. You're implementing an icall, but can't use `HANDLES()` (see the [internal calls](#internal-calls) section below)
+
+## Internal calls
+
+Prefer P/Invokes or QCalls over internal calls. That is, if your function only takes arguments that
+are not managed objects, and does not need to interact with the runtime, it is better to define it
+outside the runtime.
+
+Internal calls generally have at least one argument that is a managed object, or may throw a managed exception.
+
+Internal calls are declared in [`icall-def.h`](../../src/mono/mono/metadata/icall-def.h) See the comment in the header for details.
+
+There are two styles of internal calls: `NOHANDLES` and `HANDLES`. (This is a simplification as there
+are also JIT internal calls added by the execution engines)
+
+The difference is that `HANDLES` icalls receive references to managed objects wrapped in a handle
+that attempts to keep the object alive for the duration of the internal call even if the thread
+blocks or is suspended, while `NOHANDLES` functions don't. Additionally `HANDLES` functions get a
+`MonoError*` argument from the managed-to-native interop layer that will be converted to a managed
+exception when the function returns. `NOHANDLES` functions generally have to call
+`mono_error_set_pending_exception` themselves.
+
+## Suspend Safety
+
+See [Cooperative Suspend at mono-project.com](https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/) and the [mono thread state machine design document](../design/mono/mono-thread-state-machine.md)
+
+In general runtime functions that may be called from the public Mono API should call
+`MONO_ENTER_GC_UNSAFE`/`MONO_EXIT_GC_UNSAFE` if they call any other runtime API. Internal calls are
+already in GC Unsafe on entry, but QCalls and P/Invokes aren't.
+
+When calling a blocking native API, wrap the call in `MONO_ENTER_GC_SAFE`/`MONO_EXIT_GC_SAFE`.
+
+Prefer `mono_coop_` synchronization primitives (`MonoCoopMutex`, `MonoCoopCond`,
+`MonoCoopSemaphore`, etc) over `mono_os_` primitives (`mono_mutex_t`, `mono_cond_t`). The former
+will automatically go into GC Safe mode before doing operations that may block. The latter should
+only be used for low-level leaf locks that may need to be shared with non-runtime code. You're
+responsible for switching to GC Safe mode when doing blocking operations in that case.
+
+## GC Memory Safety
+
+We have explored many different policies on how to safely access managed memory from the runtime. The existing code is not uniform.
+
+This is the current policy (but check with a team member as this document may need to be updated):
+
+1. It is never ok to access a managed object from GC Safe code.
+2. Mono's GC scans the managed heap precisely. Mono does not allow heap objects to contain pointers into the interior of other managed obejcts.
+3. Mono's GC scans the native stack conservatively: any value that looks like a pointer into the GC
+ heap, will cause the target object to be pinned and not collected. Interior pointers from the
+ stack into the middle of a managed object are allowed and will pin the object.
+
+In general one of the following should be used to ensure that an object is kept alive, particularly
+across a call back into managed from native, or across a call that may trigger a GC (effectively
+nearly any runtime internal API, due to assembly loading potentially triggering managed callbacks):
+
+* The object is pinned in managed code using `fixed` (common with strings) and the native code gets a `gunichar2*`
+* a `ref` local is passed into native code and the native code gets a `MonoObject * volatile *` (the `volatile` is important)
+* a `Span` is passed into native code and the native code gets a `MonoSpanOfObjects*`
+* an icall is declared with `HANDLES()` and a `MonoObjectHandle` (or a more specific type such as `MonoReflectionTypeHandle` is passed in).
+* a GCHandle is passed in
+
+Generally only functions on the boundary between managed and native should use one of the above
+mechanisms (that is, it's enough that an object is pinned once). Callees can take a `MonoObject*`
+argument and assume that it was pinned by the caller.
+
+In cases where an object is created in native code, it should be kept alive:
+
+1. By assigning into a `MonoObject * volatile *` (ie: use `out` or `ref` arguments in C#)
+2. By creating a local handle using `MONO_HANDLE_NEW` or `MONO_HANDLE_PIN` (the function should then use `HANDLE_FUNCTION_ENTER`/`HANDLE_FUNCTION_RETURN` to set up and tear down a handle frame)
+3. By creating a GCHandle
+4. By assigning to a field of another managed object
+
+In all cases, if there is any intervening internal API call or a call to managed code, the object
+must be kept alive before the call using one of the above methods.
+
+### Write barriers
+
+When writing a managed object to a field of another managed object, use one of the
+`mono_gc_wbarrier_` functions (for example, `mono_gc_wbarrier_generic_store`). It is ok to call the write
+barrier functions if the destination is not in the managed heap (in which case they will just do a normal write)
+
+## Assertions
+
+Mono code should use `g_assert`, `mono_error_assert_ok`, `g_assertf`, `g_assert_not_reached` etc.
+
+Unlike CoreCLR, Mono assertions are always included in the runtime - both in Debug and in Release builds.
+
+New code should try not to rely on the side-effects of assert conditions. (That is, one day we may want
+to turn off assertions in Release builds.)
+
+## Mono Public API
+
+Mono maintains a public API for projects that embed the Mono runtime in order to provide the ability
+to execute .NET code in the context of another application or framework. The current Mono API is
+`mono-2.0`. We strive to maintain binary ABI and API stability for our embedders.
+
+The public API headers are defined in
+[`../../src/native/public/mono`](../../src/native/public/mono). Great care should be taken when
+modifying any of the functions declared in these headers. In particular breaking the ABI by
+removing these functions or changing their arguments is not allowed. Semantic changes should be
+avoided, or implemented in a way that is least disruptive to embedders (for example the runtime does
+not support multiple appdomains anymore, but `mono_domain_get` continues to work).
+
+### Hidden API functions
+
+In practice certain functions have been tagged with `MONO_API` even if they are not declared in the
+public headers. These symbols have been used by projects that embed Mono despite not being in the
+public headers. They should be treated with the same care as the real public API.
+
+### Unstable API functions
+
+Functions declared in `mono-private-unstable.h` headers do not need to maintain API/ABI stability.
+They are generally new APIs that have been added since .NET 5 but that have not been stabilized yet.
+
+As a matter of courtesy, notify the .NET macios and .NET Android teams if changing the behavior of these functions.
+
+### WASM
+
+The WASM and WASI runtimes in [`src/mono/wasm/runtime`](../../src/mono/wasm/runtime) and
+`src/mono/wasi` are effectively external API clients. When possible they should use existing `MONO_API` functions.
+
+As a matter of expedience, the wasm project has sometimes taken advantage of static linking by
+adding declarations of internal Mono functions in `src/mono/wasm/runtime/driver.c` and directly
+calling Mono internals.
+
+In general new code should not do this. When modifying existing code, mysterious WASM failures may
+be attributed to symbol signature mismatches between WASM and the Mono runtime.
diff --git a/docs/design/coreclr/botr/dac-notes.md b/docs/design/coreclr/botr/dac-notes.md
index 099807e9d5583c..64cc01fd935e02 100644
--- a/docs/design/coreclr/botr/dac-notes.md
+++ b/docs/design/coreclr/botr/dac-notes.md
@@ -15,7 +15,7 @@ The DAC infrastructure (the macros and templates that control how host or target
When one has no understanding of the DAC, it's easy to find the use of the DAC infrastructure annoying. The `TADDR`s and `PTR_this` and `dac_casts`, etc. seem to clutter the code and make it harder to understand. With just a little work, though, you'll find that these are not really difficult to learn. Keeping host and target addresses explicitly different is really a form of strong typing. The more diligent we are, the easier it becomes to ensure our code is correct.
-Because the DAC potentially operates on a dump, the part of the VM sources we build in clr.dll (msdaccore.dll) must be non-invasive. Specifically, we usually don't want to do anything that would cause writing to the target's address space, nor can we execute any code that might cause an immediate garbage collection. (If we can defer the GC, it may be possible to allocate.) Note that the _host_ state is always mutated (temporaries, stack or local heap values); it is only mutating the _target_ space that is problematic. To enforce this, we do two things: code factoring and conditional compilation. In an ideal world, we would factor the VM code so that we would strictly isolate invasive actions in functions that are separate from non-invasive functions.
+Because the DAC potentially operates on a dump, the part of the VM sources we build in mscordacwks.dll (msdaccore.dll) must be non-invasive. Specifically, we usually don't want to do anything that would cause writing to the target's address space, nor can we execute any code that might cause an immediate garbage collection. (If we can defer the GC, it may be possible to allocate.) Note that the _host_ state is always mutated (temporaries, stack or local heap values); it is only mutating the _target_ space that is problematic. To enforce this, we do two things: code factoring and conditional compilation. In an ideal world, we would factor the VM code so that we would strictly isolate invasive actions in functions that are separate from non-invasive functions.
Unfortunately, we have a large code base, most of which we wrote without ever thinking about the DAC at all. We have a significant number of functions with "find or create" semantics and many other functions that have some parts that just do inspection and other parts that write to the target. Sometimes we control this with a flag passed into the function. This is common in loader code, for example. To avoid having to complete the immense job of refactoring all the VM code before we can use the DAC, we have a second method to prevent executing invasive code from out of process. We have a defined pre-processor constant, `DACCESS_COMPILE` that we use to control what parts of the code we compile into the DAC. We would like to use the `DACCESS_COMPILE` constant as little as we can, so when we DACize a new code path, we prefer to refactor whenever possible. Thus, a function that has "find or create" semantics should become two functions: one that tries to find the information and a wrapper that calls this and creates if the find fails. That way, the DAC code path can call the find function directly and avoid the creation.
diff --git a/docs/design/libraries/LibraryImportGenerator/Compatibility.md b/docs/design/libraries/LibraryImportGenerator/Compatibility.md
index 0e3cd08727fb9c..27cf3fa45e0fd3 100644
--- a/docs/design/libraries/LibraryImportGenerator/Compatibility.md
+++ b/docs/design/libraries/LibraryImportGenerator/Compatibility.md
@@ -2,6 +2,15 @@
Documentation on compatibility guidance and the current state. The version headings act as a rolling delta between the previous version.
+## Version 2
+
+The focus of version 2 is to support all repos that make up the .NET Product, including ASP.NET Core and Windows Forms, as well as all packages in dotnet/runtime.
+
+### User defined type marshalling
+
+Support for user-defined type marshalling in the source-generated marshalling is described in [UserTypeMarshallingV2.md](UserTypeMarshallingV2.md). This support replaces the designs specified in [StructMarshalling.md](StructMarshalling.md) and [SpanMarshallers.md](SpanMarshallers.md).
+
+
## Version 1
The focus of version 1 is to support `NetCoreApp`. This implies that anything not needed by `NetCoreApp` is subject to change.
diff --git a/docs/design/libraries/LibraryImportGenerator/Pipeline.md b/docs/design/libraries/LibraryImportGenerator/Pipeline.md
index 325da5e3edb8d8..9533241c9ad294 100644
--- a/docs/design/libraries/LibraryImportGenerator/Pipeline.md
+++ b/docs/design/libraries/LibraryImportGenerator/Pipeline.md
@@ -75,11 +75,17 @@ The stub code generator itself will handle some initial setup and variable decla
1. `Pin`: data pinning in preparation for calling the generated P/Invoke
- Call `Generate` on the marshalling generator for every parameter
- Ignore any statements that are not `fixed` statements
+1. `PinnedMarshal`: conversion of managed to native data
+ - Call `Generate` on the marshalling generator for every parameter
1. `Invoke`: call to the generated P/Invoke
- Call `AsArgument` on the marshalling generator for every parameter
- Create invocation statement that calls the generated P/Invoke
-1. `KeepAlive`: keep alive any objects who's native representation won't keep them alive across the call.
+1. `NotifyForSuccessfulInvoke`: Notify a marshaller that all stages through the "Invoke" stage were successful.
+ - Used to keep alive any objects who's native representation won't keep them alive across the call.
- Call `Generate` on the marshalling generator for every parameter.
+1. `UnmarshalCapture`: capture any native out parameters to avoid memory leaks if exceptions are thrown during `Unmarshal`.
+ - If the method has a non-void return, call `Generate` on the marshalling generator for the return
+ - Call `Generate` on the marshalling generator for every parameter
1. `Unmarshal`: conversion of native to managed data
- If the method has a non-void return, call `Generate` on the marshalling generator for the return
- Call `Generate` on the marshalling generator for every parameter
@@ -97,9 +103,11 @@ try
<< Marshal >>
<< Pin >> (fixed)
{
+ << Pinned Marshal >>
<< Invoke >>
}
- << Keep Alive >>
+ << Notify For Successful Invoke >>
+ << Unmarshal Capture >>
<< Unmarshal >>
}
finally
diff --git a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
index 320c48a5d65748..d4dcae008de969 100644
--- a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
+++ b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
@@ -2,6 +2,8 @@
As part of the exit criteria for the LibraryImportGenerator experiment, we have decided to introduce support for marshalling `System.Span` and `System.ReadOnlySpan` into the LibraryImportGenerator-generated stubs. This document describes design decisions made during the implementation of these marshallers.
+> NOTE: These design docs are kept for historical purposes. The designs in this file are superseded by the designs in [UserTypeMarshallingV2.md](UserTypeMarshallingV2.md).
+
## Design 1: "Intrinsic" support for `(ReadOnly)Span`
In this design, the default support for `(ReadOnly)Span` is emitted into the marshalling stub directly and builds on the pattern we enabled for arrays.
diff --git a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
index c865ae2bae9327..18fa309d3d3fe0 100644
--- a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
+++ b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
@@ -4,6 +4,8 @@ As part of the new source-generated direction for .NET Interop, we are looking a
These types pose an interesting problem for a number of reasons listed below. With a few constraints, I believe we can create a system that will enable users to use their own user-defined types and pass them by-value to native code.
+> NOTE: These design docs are kept for historical purposes. The designs in this file are superseded by the designs in [UserTypeMarshallingV2.md](UserTypeMarshallingV2.md).
+
## Problems
- What types require marshalling and what types can be passed as-is to native code?
@@ -139,7 +141,7 @@ When these `CallerAllocatedBuffer` feature flag is present, the source generator
Type authors can pass down the `buffer` pointer to native code by using the `TwoStageMarshalling` feature to provide a `ToNativeValue` method that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. The `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span.
-### Determining if a type is doesn't need marshalling
+### Determining if a type doesn't need marshalling
For this design, we need to decide how to determine a type doesn't need to be marshalled and already has a representation we can pass directly to native code - that is, we need a definition for "does not require marshalling". We have two designs that we have experimented with below, and we have decided to go with design 2.
diff --git a/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md b/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md
new file mode 100644
index 00000000000000..072a97ab18ee5d
--- /dev/null
+++ b/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md
@@ -0,0 +1,768 @@
+# User Defined Type Marshalling for Source-Generated Interop
+
+For the V1 of our source-generator, we designed support for marshalling collection types and user-defined structure types based on the designs in [StructMarshalling.md](StructMarshalling.md) and [SpanMarshallers.md](SpanMarshallers.md). As the model was adopted throughout dotnet/runtime, ASP.NET Core, WinForms, and early adopters, we recieved substantial feedback that led us to reconsider some components of the design.
+
+Here are some of the main feedback points on the previous design:
+
+- State diagram was complex
+ - The particular order in which methods are called on the marshallers were not simple to define.
+ - Handling exception scenarios without causing memory leaks was difficult.
+ - Supporting state preservation in "element of a collection" scenarios was going to be extremely difficult.
+- Overcomplicated for simple marshallers
+ - The support for "Transparent Structures" in the original design added additional overhead on the base design instead of making this scenario cheaper.
+- Concept Overload
+ - The design with optional features and multiple shapes was getting to the point that introducing people to the design was going to be difficult as there were many small options to pick.
+- Limited specialization capabilities
+ - The V1 design mapped a single managed type to a single marshaller type. As a result, if the marshaller type required specialized support for a particular scenario such as a stack-allocated buffer optimization, then every scenario had to pay the overhead to support that conditional optimization.
+ - A marshaller could only be the marshaller for one managed type. As a result, if two types (such as `string` and `char`) both wanted to use the same marshalling concept, the developer would need to use two different marshaller types.
+
+The new design tries to address many of these concerns.
+
+The new marshallers have a stateless shape and a stateful shape. Stateful shapes are (currently) not allowed in "element of a collection" scenarios as handling them is difficult today, but we may improve this in the future. The stateful shapes are described in the order in which the methods will be called. Additionally, by moving away from using constructors for part of the marshalling, we can simplify the exception-handling guidance as we will only ever have one marshaller instance per parameter and it will always be assigned to the local.
+
+Stateless shapes avoid the problems of maintaining state and will be the primarily used shapes (they cover 90+% of our scenarios).
+
+The new stateless shapes provide simple mechanisms to implement marshalling for "Transparent Structures" without adding additional complexity.
+
+The new design has less "optional" members and each member in a shape is always used when provided.
+
+The new design uses a "marshaller entry-point" type to name a concept, which the user provides attributes on to point to the actual marshaller types per-scenario. This enables a marshaller entry-point type to provide specialized support for particular scenarios and support multiple managed types with one marshaller entry-point type.
+
+## API Diff for Supporting Attributes
+
+```diff
+namespace System.Runtime.InteropServices.Marshalling;
+
+- [AttributeUsage(AttributeTargets.Struct)]
+- public sealed class CustomTypeMarshallerAttribute : Attribute
+- {
+- public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = - CustomTypeMarshallerKind.Value)
+- {
+- ManagedType = managedType;
+- MarshallerKind = marshallerKind;
+- }
+-
+- public Type ManagedType { get; }
+- public CustomTypeMarshallerKind MarshallerKind { get; }
+- public int BufferSize { get; set; }
+- public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref;
+- public CustomTypeMarshallerFeatures Features { get; set; }
+- public struct GenericPlaceholder
+- {
+- }
+- }
+-
+- public enum CustomTypeMarshallerKind
+- {
+- Value,
+- LinearCollection
+- }
+-
+- [Flags]
+- public enum CustomTypeMarshallerFeatures
+- {
+- None = 0,
+- ///
+- /// The marshaller owns unmanaged resources that must be freed
+- ///
+- UnmanagedResources = 0x1,
+- ///
+- /// The marshaller can use a caller-allocated buffer instead of allocating in some scenarios
+- ///
+- CallerAllocatedBuffer = 0x2,
+- ///
+- /// The marshaller uses the two-stage marshalling design for its instead of the - one-stage design.
+- ///
+- TwoStageMarshalling = 0x4
+- }
+- [Flags]
+- public enum CustomTypeMarshallerDirection
+- {
+- ///
+- /// No marshalling direction
+- ///
+- [EditorBrowsable(EditorBrowsableState.Never)]
+- None = 0,
+- ///
+- /// Marshalling from a managed environment to an unmanaged environment
+- ///
+- In = 0x1,
+- ///
+- /// Marshalling from an unmanaged environment to a managed environment
+- ///
+- Out = 0x2,
+- ///
+- /// Marshalling to and from managed and unmanaged environments
+- ///
+- Ref = In | Out,
+- }
+
++ ///
++ /// Define features for a custom type marshaller.
++ ///
++ [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
++ public sealed class CustomTypeMarshallerFeaturesAttribute : Attribute
++ {
++ ///
++ /// Desired caller buffer size for the marshaller.
++ ///
++ public int BufferSize { get; set; }
++ }
++
++
++ ///
++ /// Base class attribute for custom marshaller attributes.
++ ///
++ ///
++ /// Use a base class here to allow doing ManagedToUnmanagedMarshallersAttribute.GenericPlaceholder, etc. without having 3 + separate placeholder types.
++ /// For the following attribute types, any marshaller types that are provided will be validated by an analyzer to have the + correct members to prevent
++ /// developers from accidentally typoing a member like Free() and causing memory leaks.
++ ///
++ public abstract class CustomUnmanagedTypeMarshallersAttributeBase : Attribute
++ {
++ ///
++ /// Placeholder type for generic parameter
++ ///
++ public sealed class GenericPlaceholder { }
++ }
++
++ ///
++ /// Specify marshallers used in the managed to unmanaged direction (that is, P/Invoke)
++ ///
++ [AttributeUsage(AttributeTargets.Class)]
++ public sealed class ManagedToUnmanagedMarshallersAttribute : CustomUnmanagedTypeMarshallersAttributeBase
++ {
++ ///
++ /// Create instance of .
++ ///
++ /// Managed type to marshal
++ public ManagedToUnmanagedMarshallersAttribute(Type managedType) { }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the in keyword.
++ ///
++ public Type? InMarshaller { get; set; }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the ref keyword.
++ ///
++ public Type? RefMarshaller { get; set; }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the out keyword.
++ ///
++ public Type? OutMarshaller { get; set; }
++ }
++
++ ///
++ /// Specify marshallers used in the unmanaged to managed direction (that is, Reverse P/Invoke)
++ ///
++ [AttributeUsage(AttributeTargets.Class)]
++ public sealed class UnmanagedToManagedMarshallersAttribute : CustomUnmanagedTypeMarshallersAttributeBase
++ {
++ ///
++ /// Create instance of .
++ ///
++ /// Managed type to marshal
++ public UnmanagedToManagedMarshallersAttribute(Type managedType) { }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the in keyword.
++ ///
++ public Type? InMarshaller { get; set; }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the ref keyword.
++ ///
++ public Type? RefMarshaller { get; set; }
++
++ ///
++ /// Marshaller to use when a parameter of the managed type is passed by-value or with the out keyword.
++ ///
++ public Type? OutMarshaller { get; set; }
++ }
++
++ ///
++ /// Specify marshaller for array-element marshalling and default struct field marshalling.
++ ///
++ [AttributeUsage(AttributeTargets.Class)]
++ public sealed class ElementMarshallerAttribute : CustomUnmanagedTypeMarshallersAttributeBase
++ {
++ ///
++ /// Create instance of .
++ ///
++ /// Managed type to marshal
++ /// Marshaller type to use for marshalling .
++ public ElementMarshallerAttribute(Type managedType, Type elementMarshaller) { }
++ }
++
++ ///
++ /// Specifies that a particular generic parameter is the collection element's unmanaged type.
++ ///
++ ///
++ /// If this attribute is provided on a generic parameter of a marshaller, then the generator will assume
++ /// that it is a linear collection marshaller.
++ ///
++ [AttributeUsage(AttributeTargets.GenericParameter)]
++ public sealed class ElementUnmanagedTypeAttribute : Attribute
++ {
++ }
+```
+
+## Design Details
+
+First of all, this new design continues to use the existing policy for defining "blittable" types as described in the V1 design. The rest of this document will describe the custom user-defined marshalling rules.
+
+In the new design, the user will first define an "entry-point type" that represents a marshalling concept. For example, if we are marshalling a `string` to a native UTF-8 encoded string, we might call the marshaller `Utf8StringMarshaller`. This new type will be a `static class`. The developer will then use the `ManagedToUnmanagedMarshallersAttribute`, `UnmanagedToManagedMarshallersAttribute`, and `ElementMarshallerAttribute` to specify which "marshaller implementation type" will be used to actually provide the marshalling. If an attribute is missing or a property on the attribute is set to `null` or left unset, this marshaller will not support marshalling in that scenario. A single type can be specified multiple times if it provides the marshalling support for multiple scenarios.
+
+To avoid confusion around when each marshaller applies, we define when the marshallers apply based on the C# syntax used. This helps reduce the concept load as developers don't need to remember the mapping between the previous design's `CustomTypeMarshallerDirection` enum member and the C# keyword used for a parameter, which do not match in a Reverse P/Invoke-like scenario.
+
+We will recommend that the marshaller types that are supplied are nested types of the "entry-point type" or the "entry-point type" itself, but we will not require it. Each specified marshaller type will have to abide by one of the following shapes depending on the scenario is supports.
+
+The examples below will also show which properties in each attribute support each marshaller shape.
+
+## Value Marshaller Shapes
+
+We'll start with the value marshaller shapes. These marshaller shapes support marshalling a single value.
+
+Each of these shapes will support marshalling the following type:
+
+```csharp
+// Any number of generic parameters is allowed, with any constraints
+struct TManaged
+{
+ // ...
+}
+```
+
+The type `TNative` can be any `unmanaged` type. It represents whatever unmanaged type the marshaller marshals the managed type to.
+
+### Stateless Managed->Unmanaged
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller
+{
+ public static class ManagedToNative
+ {
+ public static TNative ConvertToUnmanaged(TManaged managed); // Can throw exceptions
+
+ public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+### Stateless Managed->Unmanaged with Caller-Allocated Buffer
+
+The element type of the `Span` for the caller-allocated buffer can be any type that guarantees any alignment requirements.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller
+{
+ [CustomTypeMarshallerFeatures(BufferSize = 0x200)]
+ public static class ManagedToNative
+ {
+ public static TNative ConvertToUnmanaged(TManaged managed, Span callerAllocatedBuffer); // Can throw exceptions
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+
+### Stateless Unmanaged->Managed
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller
+{
+ public static class NativeToManaged
+ {
+ public static TManaged ConvertToManaged(TNative unmanaged); // Can throw exceptions
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+
+### Stateless Unmanaged->Managed with Guaranteed Unmarshalling
+
+This shape directs the generator to emit the `ConvertToManagedGuaranteed` call in the "GuaranteedUnmarshal" phase of marshalling.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller
+{
+ public static class NativeToManaged
+ {
+ public static TManaged ConvertToManagedGuaranteed(TNative unmanaged); // Should not throw exceptions
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+
+### Stateless Bidirectional
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ElementMarshaller(typeof(TManaged<,,,...>), typeof(Bidirectional))]
+static class TMarshaller
+{
+ public static class Bidirectional
+ {
+ // Include members from each of the following:
+ // - One Stateless Managed->Unmanaged Value shape
+ // - One Stateless Unmanaged->Managed Value shape
+ }
+}
+
+```
+
+### Stateful Managed->Unmanaged
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller
+{
+ public struct ManagedToNative // Can be ref struct
+ {
+ public ManagedToNative(); // Optional, can throw exceptions.
+
+ public void FromManaged(TManaged managed); // Can throw exceptions.
+
+ public ref TIgnored GetPinnableReference(); // Result pinned for ToUnmanaged call and Invoke, but not used otherwise.
+
+ public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public TNative ToUnmanaged(); // Can throw exceptions.
+
+ public void NotifyInvokeSucceeded(); // Optional. Should not throw exceptions.
+
+ public void Free(); // Should not throw exceptions.
+ }
+}
+
+```
+### Stateful Managed->Unmanaged with Caller Allocated Buffer
+
+The element type of the `Span` for the caller-allocated buffer can be any type that guarantees any alignment requirements.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller
+{
+ [CustomTypeMarshallerFeatures(BufferSize = 0x200)]
+ public struct ManagedToNative // Can be ref struct
+ {
+ public ManagedToNative(); // Optional, can throw exceptions.
+
+ public void FromManaged(TManaged managed, Span buffer); // Can throw exceptions.
+
+ public ref TIgnored GetPinnableReference(); // Result pinned for ToUnmanaged call and Invoke, but not used otherwise.
+
+ public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public TNative ToUnmanaged(); // Can throw exceptions.
+
+ public void NotifyInvokeSucceeded(); // Optional. Should not throw exceptions.
+
+ public void Free(); // Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Unmanaged->Managed
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller
+{
+ public struct NativeToManaged // Can be ref struct
+ {
+ public NativeToManaged(); // Optional, can throw exceptions.
+
+ public void FromUnmanaged(TNative native); // Should not throw exceptions.
+
+ public TManaged ToManaged(); // Can throw exceptions.
+
+ public void Free(); // Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Unmanaged->Managed with Guaranteed Unmarshalling
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TManaged<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller
+{
+ public struct NativeToManaged // Can be ref struct
+ {
+ public NativeToManaged(); // Optional, can throw exceptions.
+
+ public void FromUnmanaged(TNative native); // Should not throw exceptions.
+
+ public TManaged ToManagedGuaranteed(); // Should not throw exceptions.
+
+ public void Free(); // Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Bidirectional
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ManagedToUnmanagedMarshallers(typeof(TManaged<,,,...>), RefMarshaller = typeof(Bidirectional))]
+static class TMarshaller
+{
+ public struct Bidirectional // Can be ref struct
+ {
+ // Include members from each of the following:
+ // - One Stateful Managed->Unmanaged Value shape
+ // - One Stateful Unmanaged->Managed Value shape
+ }
+}
+```
+
+## Linear (Array-like) Collection Marshaller Shapes
+
+We'll continue with the collection marshaller shapes. These marshaller shapes support marshalling the structure of a collection of values, where the values themselves are marshalled with marshallers of their own (using the marshaller provided in the `ElementMarshallerAttribute`). This construction allows us to compose our marshallers and to easily support arrays of custom types without needing to implement a separate marshaller for each element type.
+
+Each of these shapes will support marshalling the following type:
+
+```csharp
+// Any number of generic parameters is allowed, with any constraints
+struct TCollection
+{
+ // ...
+}
+```
+
+A collection marshaller for a managed type will have similar generics handling as the value marshaller case; however, there is one difference. A collection marshaller must have an additional generic parameter with the `ElementUnmanagedTypeAttribute`. This parameter can optionally be constrained to `: unmanaged` (but the system will not require this). The attributed parameter will be filled in with a generics-compatible representation of the unmanaged type for the collection's element type (`nint` will be used when the native type is a pointer type).
+
+The type `TNative` can be any `unmanaged` type. It represents whatever unmanaged type the marshaller marshals the managed type to.
+
+
+### Stateless Managed->Unmanaged
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public static class ManagedToNative
+ {
+ public static TNative AllocateContainerForUnmanagedElements(TCollection managed, out int numElements); // Can throw exceptions
+
+ public static ReadOnlySpan GetManagedValuesSource(TCollection managed); // Can throw exceptions
+
+ public static Span GetUnmanagedValuesDestination(TNative nativeValue, int numElements); // Can throw exceptions
+
+ public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+### Stateless Managed->Unmanaged with Caller-Allocated Buffer
+
+The element type of the `Span` for the caller-allocated buffer can be any type that guarantees any alignment requirements, including `TUnmanagedElement`.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ [CustomTypeMarshallerFeatures(BufferSize = 0x200)]
+ public static class ManagedToNative
+ {
+ public static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span buffer, out int numElements); // Can throw exceptions
+
+ public static ReadOnlySpan GetManagedValuesSource(TCollection managed); // Can throw exceptions
+
+ public static Span GetUnmanagedValuesDestination(TNative nativeValue, int numElements); // Can throw exceptions
+
+ public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public static void Free(TNative unmanaged); // Optional. Should not throw exceptions
+ }
+}
+
+```
+
+### Stateless Unmanaged->Managed
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public static class NativeToManaged
+ {
+ public static TCollection AllocateContainerForManagedElements(int length); // Can throw exceptions
+
+ public static Span GetManagedValuesDestination(T[] managed) => managed; // Can throw exceptions
+
+ public static ReadOnlySpan GetUnmanagedValuesSource(TNative nativeValue, int numElements); // Can throw exceptions
+
+ public static void Free(TNative native); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateless Unmanaged->Managed with Guaranteed Unmarshalling
+
+This shape directs the generator to emit the `ConvertToManagedGuaranteed` call in the "GuaranteedUnmarshal" phase of marshalling.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public static class NativeToManaged
+ {
+ public static TCollection AllocateContainerForManagedElementsGuaranteed(int length); // Should not throw exceptions other than OutOfMemoryException.
+
+ public static Span GetManagedValuesDestination(T[] managed) => managed; // Can throw exceptions
+
+ public static ReadOnlySpan GetUnmanagedValuesSource(TNative nativeValue, int numElements); // Can throw exceptions
+
+ public static void Free(TNative native); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateless Bidirectional
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ElementMarshaller(typeof(TManaged<,,,...>), typeof(Bidirectional))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public static class Bidirectional
+ {
+ // Include members from each of the following:
+ // - One Stateless Managed->Unmanaged Linear Collection shape
+ // - One Stateless Unmanaged->Managed Linear Collection shape
+ }
+}
+
+```
+
+### Stateful Managed->Unmanaged
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public struct ManagedToNative // Can be ref struct
+ {
+ public ManagedToNative(); // Optional, can throw exceptions.
+
+ public void FromManaged(TCollection collection); // Can throw exceptions.
+
+ public ReadOnlySpan GetManagedValuesSource(); // Can throw exceptions.
+
+ public Span GetNativeValuesDestination(); // Can throw exceptions.
+
+ public ref TIgnored GetPinnableReference(); // Optional. Can throw exceptions.
+
+ public TNative ToUnmanaged(); // Can throw exceptions.
+
+ public static ref TOther GetPinnableReference(TCollection collection); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public void NotifyInvokeSucceeded(); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+### Stateful Managed->Unmanaged with Caller Allocated Buffer
+
+The element type of the `Span` for the caller-allocated buffer can be any type that guarantees any alignment requirements.
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(ManagedToNative))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(ManagedToNative))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ [CustomTypeMarshallerFeatures(BufferSize = 0x200)]
+ public struct ManagedToNative // Can be ref struct
+ {
+ public ManagedToNative(); // Optional, can throw exceptions.
+
+ public void FromManaged(TCollection collection, Span buffer); // Can throw exceptions.
+
+ public ReadOnlySpan GetManagedValuesSource(); // Can throw exceptions.
+
+ public Span GetNativeValuesDestination(); // Can throw exceptions.
+
+ public ref TIgnored GetPinnableReference(); // Optional. Can throw exceptions.
+
+ public TNative ToUnmanaged(); // Can throw exceptions.
+
+ public static ref TOther GetPinnableReference(TCollection collection); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
+
+ public void NotifyInvokeSucceeded(); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Unmanaged->Managed
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public struct NativeToManaged // Can be ref struct
+ {
+ public NativeToManaged(); // Optional, can throw exceptions.
+
+ public void FromUnmanaged(TNative value); // Should not throw exceptions.
+
+ public ReadOnlySpan GetNativeValuesSource(int length); // Can throw exceptions.
+
+ public Span GetManagedValuesDestination(int length); // Can throw exceptions.
+
+ public TCollection ToManaged(); // Can throw exceptions
+
+ public void Free(); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Unmanaged->Managed with Guaranteed Unmarshalling
+
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), OutMarshaller = typeof(NativeToManaged))]
+[UnmanagedToManagedMarshallers(typeof(TCollection<,,,...>), InMarshaller = typeof(NativeToManaged))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public struct NativeToManaged // Can be ref struct
+ {
+ public NativeToManaged(); // Optional, can throw exceptions.
+
+ public void FromUnmanaged(TNative value); // Should not throw exceptions.
+
+ public ReadOnlySpan GetNativeValuesSource(int length); // Can throw exceptions.
+
+ public Span GetManagedValuesDestination(int length); // Can throw exceptions.
+
+ public TCollection ToManagedGuaranteed(); // Can throw exceptions
+
+ public void Free(); // Optional. Should not throw exceptions.
+ }
+}
+
+```
+
+### Stateful Bidirectional
+```csharp
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), RefMarshaller = typeof(Bidirectional))]
+[ManagedToUnmanagedMarshallers(typeof(TCollection<,,,...>), RefMarshaller = typeof(Bidirectional))]
+static class TMarshaller where TUnmanagedElement : unmanaged
+{
+ public struct Bidirectional // Can be ref struct
+ {
+ // Include members from each of the following:
+ // - One Stateful Managed->Unmanaged Linear Collection shape
+ // - One Stateful Unmanaged->Managed Linear Collection shape
+ }
+}
+```
+
+## Optional Members In Shapes
+
+There's a few optional members in the above shapes. This section explains what these members do and why they're optional.
+
+### Free method
+
+The `Free` method on each shape supports releasing any unmanaged (or managed in the stateful shapes) resources. This method is optional as the `Free` method is required to be called in a `finally` clause and emitting a `try-finally` block with only method calls to empty methods puts a lot of stress on the JIT to inline all of the methods and realize that they are no-ops to remove the `finally` clause. Additionally, just having the `try-finally` block wrapping the main code can cause some de-optimizations.
+
+### NotifyInvokeSucceeded method
+
+This method is called after a stub successfully invokes the target code (unmanaged code in a P/Invoke scenario, managed code in a Reverse P/Invoke scenario). As this method would be called in a very large majority of cases in P/Invoke-style scenarios and has only limited utility (its main use is to provide a good place to call `GC.KeepAlive` that does not require a `try-finally` block), we decided to make it optional.
+
+### Instance GetPinnableReference method on stateful shapes
+
+The non-static `GetPinnableReference` method on stateful shapes is provided to enable pinning a managed value as part of the marshalling process. As some types don't have values that need to be pinned to help with marshalling and pinning has some overhead, this member is optional to make the overhead pay-for-play.
+
+### Static GetPinnableReference method
+
+The static GetPinnableReference method provides a mechanism to pin a managed value and pass down the pinned value directly to native code. This allows us to provide massive performance benefits and to match built-in interop semantics. Unlike the previous design that used the `GetPinnableReference` method on the managed type in some scenarios, this design allows the "interop" pinning rules to not match the easier-to-use `GetPinnableReference` instance method, which may have differing semantics (`Span` and arrays being a prime example here). As many types aren't marshallable via only pinning, the generator does not require this method on every marshaller.
+
+### `-Generated` method variants
+
+These method variants provide a mechanism for a marshaller to state that it needs to be called during the "Generated Unmarshal" phase in the `finally` block to ensure that resources are not leaked. This feature is required only by the SafeHandle marshaller, so it is an optional extension to the model instead of being a required feature.
+
+## Blittability
+
+To determine which types are blittable and which are not, we will be following [Design 2 in StructMarshalling.md](StructMarshalling.md#determining-if-a-type-doesnt-need-marshalling).
+
+## Using the marshallers
+
+To use these marshallers the user would apply either the `NativeMarshallingAttribute` attribute to their type or a `MarshalUsingAttribute` at the marshalling location (field, parameter, or return value) with a marshalling type matching the same requirements as `NativeMarshallingAttribute`'s marshalling type.
+
+The marshaller type must be an entry-point marshaller type as defined above and meet the following additional requirements:
+
+- The type must either be:
+ - Non-generic
+ - A closed generic
+ - An open generic with as many generic parameters with compatible constraints as the managed type (excluding up to one generic parameter with the `ElementUnmanagedTypeAttribute`)
+- If used in `NativeMarshallingAttribute`, the type should be at least as visible as the managed type.
+
+Passing size info for parameters will be based to the [V1 design](SpanMarshallers.md#providing-additional-data-for-collection-marshalling) and the properties/fields on `MarshalUsingAttribute` will remain unchanged.
+
+Here are some examples of using these new marshaller shapes with the `NativeMarshallingAttribute` and the `MarshalUsingAttribute`.
+
+```csharp
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+[NativeMarshalling(typeof(HResultMarshaller))]
+struct HResult
+{
+ private int hr;
+}
+
+[ManagedToUnmanagedMarshallers(typeof(HResult), InMarshaller = typeof(HResultMarshaller), RefMarshaller = typeof(HResultMarshaller), OutMarshaller = typeof(HResultMarshaller))]
+[UnmanagedToManagedMarshallers(typeof(HResult), InMarshaller = typeof(HResultMarshaller), RefMarshaller = typeof(HResultMarshaller), OutMarshaller = typeof(HResultMarshaller))]
+[ElementMarshaller(typeof(HResult), typeof(HResultMarshaller))]
+public static class HResultMarshaller
+{
+ public static int ConvertToUnmanaged(HResult hr);
+ public static HResult ConvertToManaged(int hr);
+}
+
+public static class NativeLib
+{
+ [LibraryImport(nameof(NativeLib))]
+ public static partial HResult CountArrayElements(
+ [MarshalUsing(typeof(ArrayMarshaller<,>))] int[] array, // Unlike the V1 system, we'll allow open generics in the V2 system in MarshalUsing since there's an extra generic parameter that the user does not provide.
+ out int numElements);
+}
+
+```
diff --git a/docs/design/security/unix-tmp.md b/docs/design/security/unix-tmp.md
new file mode 100644
index 00000000000000..bea8cb8f11ae6a
--- /dev/null
+++ b/docs/design/security/unix-tmp.md
@@ -0,0 +1,45 @@
+
+# Unix temporary files
+
+The Unix support for temporary files is different from the Windows model and developers who
+are used to Windows may inadvertently create security risk if they use the same practices on Unix.
+
+Most notably, the Windows model for temporary files is that the operating system provides each user with a *unique*, *user-owned* temporary directory.
+Moreover, all Windows users, including the service and system users, have designated user folders, including temporary folders.
+
+The Unix model is very different. The temp directory, assuming there is one, is often a global folder (except on MacOS).
+If possible, prefer a library function like `GetTempPath()` to find the folder. Otherwise,
+the `TMPDIR` environment variable is used to store the location of this folder. This variable is
+widely used and supported, but it is not mandatory for all Unix implementations. It should be the preferred
+mechanism for finding the Unix temporary folder if a library method is not available. It will commonly
+point to either the `/tmp` or `/var/tmp` folder. These folders are not used for MacOS, so it is not recommended
+to use them directly.
+
+Because the temporary directory is often global, any use of the temp directory should be carefully
+considered. In general, the best use of the temp directory is for programs which,
+
+1. Will create the temporary file during their process execution
+1. Do not depend on predictable temporary file/folder names
+1. Will not access the file after the process exits
+
+In these cases, the process can create a file or files with
+ 1. A pseudorandom name, unlikely to cause collisions
+ 1. Permissions which restrict all access to owner-only, i.e. 700 for directories, 600 for files
+
+Any other use needs to be carefully audited, particularly if the temporary file is intended for use across
+multiple processes. Some considerations:
+
+- **Never** write files with global access permissions
+- **Always** verify that the owner of the file is the current user and that the permissions
+ only allow write access by the owner when reading existing files
+- **Never** rely on having ownership of a particular file name. Any process can write a file with that name,
+ creating a denial of service.
+ - When creating files, consider likelihood of file name collision and performance impact of attempting
+ to create new names, if supported.
+
+ If any of the above conflict with the feature requirements, consider instead writing temporary files to a
+ location in the user home folder. Some considerations for this model:
+
+ - There is no automatic cleanup in user folders. Files will remain permanently or require cleanup by the app
+ - Some environments do not have user home folders (e.g., systemd). Consider providing an environment variable
+ to override the location of the temporary folder, and provide user documentation for this variable.
diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md
index c0e56c9fa04cf0..876d4a62f58aaa 100644
--- a/docs/design/specs/Ecma-335-Augments.md
+++ b/docs/design/specs/Ecma-335-Augments.md
@@ -17,6 +17,7 @@ This is a list of additions and edits to be made in ECMA-335 specifications. It
- [Rules for IL rewriters](#rules-for-il-rewriters)
- [Checked user-defined operators](#checked-user-defined-operators)
- [Atomic reads and writes](#atomic-reads-and-writes)
+- [Backward branch constraints](#backward-branch-constraints)
## Signatures
@@ -1020,3 +1021,7 @@ A checked user-defined operator is expected to throw an exception when the resul
Section "I.12.6.6 Atomic reads and writes" adds clarification that the atomicity guarantees apply to built-in primitive value types and pointers only.
A conforming CLI shall guarantee that read and write access of *built-in primitive value types and pointers* to properly aligned memory locations no larger than the native word size (the size of type native int) is atomic (see §I.12.6.2) when all the write accesses to a location are the same size.
+
+## Backward branch constraints
+
+Section "II.1.7.5 Backward branch constraints" is deleted. These constraints were not enforced by any mainstream .NET runtime and they are not respected by .NET compilers. It means that it is not possible to infer the exact state of the evaluation stack at every instruction with a single forward-pass through the CIL instruction stream.
diff --git a/docs/project/dogfooding.md b/docs/project/dogfooding.md
index 3ff4160095b1e9..69f405a7f80764 100644
--- a/docs/project/dogfooding.md
+++ b/docs/project/dogfooding.md
@@ -19,9 +19,9 @@ dotnet nuget add source -n dotnet7 https://dnceng.pkgs.visualstudio.com/public/_
Then, you will be able to add the latest prerelease version of the desired package to your project.
-**Example:** To add version 7.0.100-preview.5.22226.4 of the System.Data.OleDb package, use the [dotnet add package](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package) command:
+**Example:** To add version 7.0-preview.5.22226.4 of the System.Data.OleDb package, use the [dotnet add package](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package) command:
```
-dotnet add package System.Data.OleDb -v 7.0.100-preview.5.22226.4
+dotnet add package System.Data.OleDb -v 7.0-preview.5.22226.4
```
To use nightly builds of the entire runtime, follow the steps given in the rest of this document instead.
@@ -30,9 +30,7 @@ To use nightly builds of the entire runtime, follow the steps given in the rest
1. Acquire the latest nightly .NET SDK by downloading and extracting a zip/tarball or using an installer from the [installers and binaries table in dotnet/installer](https://github.com/dotnet/installer#installers-and-binaries) (for example, https://aka.ms/dotnet/7.0/daily/dotnet-sdk-win-x64.zip).
-2. By default, the dotnet CLI will use the globally installed SDK if it matches the major/minor version you request and has a higher revision. To force it to use a locally installed SDK, you must set an environment variable `DOTNET_MULTILEVEL_LOOKUP=0` in your shell. You can use `dotnet --info` to verify what version of the Shared Framework it is using.
-
-3. Reminder: if you are using a local copy of the dotnet CLI, take care that when you type `dotnet` you do not inadvertently pick up a different copy that you may have in your path. On Windows, for example, if you use a Command Prompt, a global copy may be in the path, so use the fully qualified path to your local `dotnet` (e.g. `C:\dotnet\dotnet.exe`). If you receive an error "error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0." then you may be executing an older `dotnet`.
+2. If you are using a local copy of the dotnet CLI, take care that when you type `dotnet` you do not inadvertently pick up a different copy that you may have in your path. On Windows, for example, if you use a Command Prompt, a global copy may be in the path, so use the fully qualified path to your local `dotnet` (e.g. `C:\dotnet\dotnet.exe`). If you receive an error "error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0." then you may be executing an older `dotnet`.
After setting up dotnet you can verify you are using the dogfooding version by executing `dotnet --info`. Here is an example output at the time of writing:
```
@@ -70,11 +68,10 @@ Learn about .NET Runtimes and SDKs:
```
-4. Our nightly builds are uploaded to dotnet-blob feeds, not NuGet - so ensure the .NET Core blob feed is in your nuget configuration in case you need other packages from .NET Core that aren't included in the download. For example, on Windows you could edit `%userprofile%\appdata\roaming\nuget\nuget.config` or on Linux edit `~/.nuget/NuGet/NuGet.Config` to add these lines:
+3. Our nightly builds are uploaded to nightly feed, not NuGet - so ensure the nightly feed is in your nuget configuration in case you need other packages that aren't included in the download. For example, on Windows you could edit `%userprofile%\appdata\roaming\nuget\nuget.config` or on Linux edit `~/.nuget/NuGet/NuGet.Config` to add these lines:
```xml
-
-
+
...
```
@@ -145,7 +142,7 @@ make it self-contained by adding a RuntimeIdentifier (RID).
net7.0
-
+
7.0.0-preview.5.22224.3
win-x64
diff --git a/eng/CodeAnalysis.src.globalconfig b/eng/CodeAnalysis.src.globalconfig
index a9a8f29956cd27..95f58b409563a3 100644
--- a/eng/CodeAnalysis.src.globalconfig
+++ b/eng/CodeAnalysis.src.globalconfig
@@ -411,6 +411,9 @@ dotnet_diagnostic.CA1852.severity = warning
# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)'
dotnet_diagnostic.CA1853.severity = warning
+# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
+dotnet_diagnostic.CA1854.severity = warning
+
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = none
@@ -1070,8 +1073,8 @@ dotnet_diagnostic.SA1204.severity = none
# SA1205: Partial elements should declare an access modifier
dotnet_diagnostic.SA1205.severity = warning
-# SA1206: Keyword ordering
-dotnet_diagnostic.SA1206.severity = warning
+# SA1206: Keyword ordering - TODO Re-enable as warning after https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3527
+dotnet_diagnostic.SA1206.severity = suggestion
# SA1208: Using directive ordering
dotnet_diagnostic.SA1208.severity = none
@@ -1353,7 +1356,7 @@ dotnet_diagnostic.IDE0018.severity = suggestion
dotnet_diagnostic.IDE0019.severity = suggestion
# IDE0020: Use pattern matching to avoid is check followed by a cast (with variable)
-dotnet_diagnostic.IDE0020.severity = suggestion
+dotnet_diagnostic.IDE0020.severity = warning
# IDE0021: Use expression body for constructors
dotnet_diagnostic.IDE0021.severity = silent
@@ -1380,13 +1383,13 @@ dotnet_diagnostic.IDE0027.severity = silent
dotnet_diagnostic.IDE0028.severity = suggestion
# IDE0029: Use coalesce expression
-dotnet_diagnostic.IDE0029.severity = suggestion
+dotnet_diagnostic.IDE0029.severity = warning
# IDE0030: Use coalesce expression
-dotnet_diagnostic.IDE0030.severity = suggestion
+dotnet_diagnostic.IDE0030.severity = warning
# IDE0031: Use null propagation
-dotnet_diagnostic.IDE0031.severity = silent
+dotnet_diagnostic.IDE0031.severity = warning
# IDE0032: Use auto property
dotnet_diagnostic.IDE0032.severity = silent
@@ -1401,7 +1404,7 @@ dotnet_diagnostic.IDE0034.severity = suggestion
dotnet_diagnostic.IDE0035.severity = suggestion
# IDE0036: Order modifiers
-dotnet_diagnostic.IDE0036.severity = suggestion
+dotnet_diagnostic.IDE0036.severity = warning
# IDE0037: Use inferred member name
dotnet_diagnostic.IDE0037.severity = silent
@@ -1455,7 +1458,7 @@ dotnet_diagnostic.IDE0052.severity = suggestion
dotnet_diagnostic.IDE0053.severity = silent
# IDE0054: Use compound assignment
-dotnet_diagnostic.IDE0054.severity = suggestion
+dotnet_diagnostic.IDE0054.severity = warning
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = suggestion
@@ -1489,7 +1492,7 @@ dotnet_diagnostic.IDE0063.severity = silent
dotnet_diagnostic.IDE0064.severity = silent
# IDE0065: Misplaced using directive
-dotnet_diagnostic.IDE0065.severity = suggestion
+dotnet_diagnostic.IDE0065.severity = warning
# IDE0066: Convert switch statement to expression
dotnet_diagnostic.IDE0066.severity = suggestion
@@ -1498,7 +1501,7 @@ dotnet_diagnostic.IDE0066.severity = suggestion
dotnet_diagnostic.IDE0070.severity = suggestion
# IDE0071: Simplify interpolation
-dotnet_diagnostic.IDE0071.severity = suggestion
+dotnet_diagnostic.IDE0071.severity = warning
# IDE0072: Add missing cases
dotnet_diagnostic.IDE0072.severity = silent
@@ -1543,10 +1546,10 @@ dotnet_diagnostic.IDE0084.severity = none
dotnet_diagnostic.IDE0090.severity = silent
# IDE0100: Remove redundant equality
-dotnet_diagnostic.IDE0100.severity = suggestion
+dotnet_diagnostic.IDE0100.severity = warning
# IDE0110: Remove unnecessary discard
-dotnet_diagnostic.IDE0110.severity = suggestion
+dotnet_diagnostic.IDE0110.severity = warning
# IDE0120: Simplify LINQ expression
dotnet_diagnostic.IDE0120.severity = none
@@ -1567,7 +1570,7 @@ dotnet_diagnostic.IDE0160.severity = silent
dotnet_diagnostic.IDE0161.severity = silent
# IDE1005: Delegate invocation can be simplified.
-dotnet_diagnostic.IDE1005.severity = suggestion
+dotnet_diagnostic.IDE1005.severity = warning
# IDE1006: Naming styles
dotnet_diagnostic.IDE1006.severity = silent
diff --git a/eng/CodeAnalysis.test.globalconfig b/eng/CodeAnalysis.test.globalconfig
index 97bf6113e88e76..59fc4e0022deba 100644
--- a/eng/CodeAnalysis.test.globalconfig
+++ b/eng/CodeAnalysis.test.globalconfig
@@ -408,6 +408,9 @@ dotnet_diagnostic.CA1852.severity = none
# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)'
dotnet_diagnostic.CA1853.severity = none
+# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
+dotnet_diagnostic.CA1854.severity = none
+
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = none
diff --git a/eng/Subsets.props b/eng/Subsets.props
index 4fbd6b5e3fbf66..1d08c11b1013a2 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -27,7 +27,7 @@
flavor is used to decide when to build the hosts and installers. -->
CoreCLR
- Mono
+ Mono
@@ -312,6 +312,8 @@
Test="true" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/>
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index df1d92585c1e8e..d7a0af994c93ea 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,16 +1,16 @@
-
+
https://github.com/dotnet/icu
- 7c13e1a1740e54ba0c9b38636ae36195c9e9d3bd
+ 8ea608427e5988d3dd25130400947aad0a917bba
https://github.com/dotnet/msquic
b4d67ca60d3f819e2450095ab8a33a9f65513e4a
-
+
https://github.com/dotnet/emsdk
- ea10b4e5534de1806cc2e84ddd3b00eabcab962f
+ 3fbbd97b47b5a71d7cf3ca2e197b0b93feaf975d
https://github.com/dotnet/wcf
@@ -54,129 +54,129 @@
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
https://github.com/microsoft/vstest
140434f7109d357d0158ade9e5164a4861513965
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
https://github.com/dotnet/llvm-project
@@ -210,53 +210,53 @@
https://github.com/dotnet/llvm-project
54cc196d506692c366d9e116cdb3a9a56342f720
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/runtime
- f21ace52e357bbf0019da5c9e42d66705a087235
+ 2b0d0d164aac758b1181329f97c87819e203617c
-
+
https://github.com/dotnet/linker
- 1481a51970586b26208a7bc6173dc77d658f3508
+ 17033869c8e75e3805ba42af4c5509af72c8bb72
-
+
https://github.com/dotnet/xharness
- a1d9a67e971fc0b8724507847491fe93f65728db
+ 15aa00a936f7ac3f25358a82e795df12bc7710da
-
+
https://github.com/dotnet/xharness
- a1d9a67e971fc0b8724507847491fe93f65728db
+ 15aa00a936f7ac3f25358a82e795df12bc7710da
-
+
https://github.com/dotnet/xharness
- a1d9a67e971fc0b8724507847491fe93f65728db
+ 15aa00a936f7ac3f25358a82e795df12bc7710da
-
+
https://github.com/dotnet/arcade
- ba1c3aff4be864c493031d989259ef92aaa23fc3
+ ccfe6da198c5f05534863bbb1bff66e830e0c6ab
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
@@ -278,13 +278,13 @@
https://github.com/dotnet/hotreload-utils
3c641f5b79f90b0341bc0b6f728bae56ede711fd
-
+
https://github.com/dotnet/runtime-assets
- 0920468fa7db4ee8ea8bbcba186421cb92713adf
+ 371af1f99788b76eae14b96aad4ab7ac9b373938
-
+
https://github.com/dotnet/roslyn-analyzers
- 114d5f2927b8afc90f169df80fdcbe8c7a644bac
+ 0e7f40d1709f0d7ffd316d89e452151a7365354b
https://github.com/dotnet/sdk
diff --git a/eng/Versions.props b/eng/Versions.props
index 981629491f417a..865ba86f68d0f5 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -44,40 +44,40 @@
4.3.0-1.22206.2
4.3.0-1.22206.2
4.3.0-1.22206.2
- 7.0.0-preview1.22302.1
+ 7.0.0-preview1.22310.1
4.3.0-1.22206.2
- 4.3.0-3.22281.14
+ 4.4.0-1.22315.13
2.0.0-preview.4.22252.4
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 2.5.1-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
- 7.0.0-beta.22255.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 2.5.1-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
+ 7.0.0-beta.22316.2
6.0.0-preview.1.102
- 7.0.0-preview.6.22305.4
- 7.0.0-preview.6.22305.4
- 7.0.0-preview.6.22305.4
+ 7.0.0-preview.6.22319.5
+ 7.0.0-preview.6.22319.5
+ 7.0.0-preview.6.22319.5
3.1.0
- 7.0.0-preview.6.22305.4
+ 7.0.0-preview.6.22319.5
1.0.0-alpha.1.22252.1
1.0.0-alpha.1.22252.1
1.0.0-alpha.1.22252.1
@@ -111,25 +111,25 @@
5.0.0
5.0.0
4.9.0
- 7.0.0-preview.6.22305.4
+ 7.0.0-preview.6.22319.5
6.0.0
4.5.4
4.5.0
- 7.0.0-preview.6.22305.4
+ 7.0.0-preview.6.22319.5
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
- 7.0.0-beta.22281.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
+ 7.0.0-beta.22313.1
1.0.0-prerelease.22279.1
1.0.0-prerelease.22279.1
@@ -150,9 +150,9 @@
1.1.0
16.9.0-preview-20201201-01
- 1.0.0-prerelease.22305.1
- 1.0.0-prerelease.22305.1
- 1.0.0-prerelease.22305.1
+ 1.0.0-prerelease.22320.3
+ 1.0.0-prerelease.22320.3
+ 1.0.0-prerelease.22320.3
1.1.0-alpha.0.22306.2
2.4.2-pre.22
0.12.0-pre.20
@@ -168,10 +168,10 @@
7.0.0-preview-20220608.1
- 7.0.100-1.22306.1
+ 7.0.100-1.22320.3
$(MicrosoftNETILLinkTasksVersion)
- 7.0.0-preview.6.22306.1
+ 7.0.0-preview.6.22320.2
7.0.0-alpha.1.22301.1
@@ -184,7 +184,7 @@
11.1.0-alpha.1.22259.2
11.1.0-alpha.1.22259.2
- 7.0.0-preview.6.22281.1
+ 7.0.0-preview.6.22320.1
$(MicrosoftNETWorkloadEmscriptenManifest70100Version)
1.1.87-gba258badda
diff --git a/eng/build.sh b/eng/build.sh
index 9c9beb471f1fb1..21784abd80be38 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -17,7 +17,7 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
usage()
{
echo "Common settings:"
- echo " --arch (-a) Target platform: x86, x64, arm, armv6, armel, arm64, loongarch64, s390x or wasm."
+ echo " --arch (-a) Target platform: x86, x64, arm, armv6, armel, arm64, loongarch64, s390x, ppc64le or wasm."
echo " [Default: Your machine's architecture.]"
echo " --binaryLog (-bl) Output binary log."
echo " --cross Optional argument to signify cross compilation."
@@ -206,12 +206,12 @@ while [[ $# > 0 ]]; do
fi
passedArch="$(echo "$2" | tr "[:upper:]" "[:lower:]")"
case "$passedArch" in
- x64|x86|arm|armv6|armel|arm64|loongarch64|s390x|wasm)
+ x64|x86|arm|armv6|armel|arm64|loongarch64|s390x|ppc64le|wasm)
arch=$passedArch
;;
*)
echo "Unsupported target architecture '$2'."
- echo "The allowed values are x86, x64, arm, armv6, armel, arm64, loongarch64, s390x, and wasm."
+ echo "The allowed values are x86, x64, arm, armv6, armel, arm64, loongarch64, s390x, ppc64le and wasm."
exit 1
;;
esac
diff --git a/eng/common/init-tools-native.ps1 b/eng/common/init-tools-native.ps1
index 413adea4365b12..24a5e65de1b3e3 100644
--- a/eng/common/init-tools-native.ps1
+++ b/eng/common/init-tools-native.ps1
@@ -93,7 +93,7 @@ try {
$ToolVersion = ""
}
$ArcadeToolsDirectory = "C:\arcade-tools"
- if (Test-Path $ArcadeToolsDirectory -eq $False) {
+ if (-not (Test-Path $ArcadeToolsDirectory)) {
Write-Error "Arcade tools directory '$ArcadeToolsDirectory' was not found; artifacts were not properly installed."
exit 1
}
@@ -103,13 +103,14 @@ try {
exit 1
}
$BinPathFile = "$($ToolDirectory.FullName)\binpath.txt"
- if (Test-Path -Path "$BinPathFile" -eq $False) {
+ if (-not (Test-Path -Path "$BinPathFile")) {
Write-Error "Unable to find binpath.txt in '$($ToolDirectory.FullName)' ($ToolName $ToolVersion); artifact is either installed incorrectly or is not a bootstrappable tool."
exit 1
}
$BinPath = Get-Content "$BinPathFile"
- Write-Host "Adding $ToolName to the path ($(Convert-Path -Path $BinPath))..."
- Write-Host "##vso[task.prependpath]$(Convert-Path -Path $BinPath)"
+ $ToolPath = Convert-Path -Path $BinPath
+ Write-Host "Adding $ToolName to the path ($ToolPath)..."
+ Write-Host "##vso[task.prependpath]$ToolPath"
}
}
exit 0
@@ -188,7 +189,7 @@ try {
Write-Host "##vso[task.prependpath]$(Convert-Path -Path $InstallBin)"
return $InstallBin
}
- else {
+ elseif (-not ($PathPromotion)) {
Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message 'Native tools install directory does not exist, installation failed'
exit 1
}
diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh
index 4b99a9cad3b772..6d7ba15e5f2b5d 100644
--- a/eng/common/native/init-compiler.sh
+++ b/eng/common/native/init-compiler.sh
@@ -71,7 +71,7 @@ if [[ -z "$CLR_CC" ]]; then
# Set default versions
if [[ -z "$majorVersion" ]]; then
# note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero.
- if [[ "$compiler" == "clang" ]]; then versions=( 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 )
+ if [[ "$compiler" == "clang" ]]; then versions=( 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 )
elif [[ "$compiler" == "gcc" ]]; then versions=( 12 11 10 9 8 7 6 5 4.9 ); fi
for version in "${versions[@]}"; do
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 797f05292a8515..423bd962e9661b 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -635,6 +635,10 @@ function InitializeNativeTools() {
InstallDirectory = "$ToolsDir"
}
}
+ if (Test-Path variable:NativeToolsOnMachine) {
+ Write-Host "Variable NativeToolsOnMachine detected, enabling native tool path promotion..."
+ $nativeArgs += @{ PathPromotion = $true }
+ }
& "$PSScriptRoot/init-tools-native.ps1" @nativeArgs
}
}
diff --git a/eng/native/build-commons.sh b/eng/native/build-commons.sh
index dca61277f7fff1..1b28031ab4de49 100755
--- a/eng/native/build-commons.sh
+++ b/eng/native/build-commons.sh
@@ -197,7 +197,7 @@ usage()
echo ""
echo "Common Options:"
echo ""
- echo "BuildArch can be: -arm, -armv6, -armel, -arm64, -loongarch64, -s390x, x64, x86, -wasm"
+ echo "BuildArch can be: -arm, -armv6, -armel, -arm64, -loongarch64, -s390x, -ppc64le, x64, x86, -wasm"
echo "BuildType can be: -debug, -checked, -release"
echo "-os: target OS (defaults to running OS)"
echo "-bindir: output directory (defaults to $__ProjectRoot/artifacts)"
@@ -392,6 +392,10 @@ while :; do
__TargetArch=wasm
;;
+ ppc64le|-ppc64le)
+ __TargetArch=ppc64le
+ ;;
+
os|-os)
if [[ -n "$2" ]]; then
__TargetOS="$2"
diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index 931cecdeddb5b6..047999bded88ff 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -236,6 +236,9 @@ elseif (CLR_CMAKE_HOST_ARCH_WASM)
elseif (CLR_CMAKE_HOST_ARCH_MIPS64)
set(ARCH_HOST_NAME mips64)
add_definitions(-DHOST_MIPS64 -DHOST_64BIT=1)
+elseif (CLR_CMAKE_HOST_ARCH_POWERPC64)
+ set(ARCH_HOST_NAME ppc64le)
+ add_definitions(-DHOST_POWERPC64 -DHOST_64BIT)
else ()
clr_unknown_arch()
endif ()
@@ -256,6 +259,8 @@ if (CLR_CMAKE_HOST_UNIX)
message("Detected Linux i686")
elseif(CLR_CMAKE_HOST_UNIX_S390X)
message("Detected Linux s390x")
+ elseif(CLR_CMAKE_HOST_UNIX_POWERPC64)
+ message("Detected Linux ppc64le")
else()
clr_unknown_arch()
endif()
@@ -332,6 +337,11 @@ elseif (CLR_CMAKE_TARGET_ARCH_S390X)
set(ARCH_SOURCES_DIR s390x)
add_compile_definitions($<$>>:TARGET_S390X>)
add_compile_definitions($<$>>:TARGET_64BIT>)
+elseif (CLR_CMAKE_TARGET_ARCH_POWERPC64)
+ set(ARCH_TARGET_NAME ppc64le)
+ set(ARCH_SOURCES_DIR ppc64le)
+ add_compile_definitions($<$>>:TARGET_POWERPC64>)
+ add_compile_definitions($<$>>:TARGET_64BIT>)
elseif (CLR_CMAKE_TARGET_ARCH_WASM)
set(ARCH_TARGET_NAME wasm)
set(ARCH_SOURCES_DIR wasm)
diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake
index 573d57f9ed0ddd..d21206f1f8bcad 100644
--- a/eng/native/configureplatform.cmake
+++ b/eng/native/configureplatform.cmake
@@ -51,6 +51,8 @@ if(CLR_CMAKE_HOST_OS STREQUAL Linux)
set(CLR_CMAKE_HOST_UNIX_X86 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL s390x)
set(CLR_CMAKE_HOST_UNIX_S390X 1)
+ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le)
+ set(CLR_CMAKE_HOST_UNIX_POWERPC64 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL mips64)
set(CLR_CMAKE_HOST_UNIX_MIPS64 1)
else()
@@ -250,6 +252,9 @@ elseif(CLR_CMAKE_HOST_UNIX_X86)
elseif(CLR_CMAKE_HOST_UNIX_S390X)
set(CLR_CMAKE_HOST_ARCH_S390X 1)
set(CLR_CMAKE_HOST_ARCH "s390x")
+elseif(CLR_CMAKE_HOST_UNIX_POWERPC64)
+ set(CLR_CMAKE_HOST_ARCH_POWERPC64 1)
+ set(CLR_CMAKE_HOST_ARCH "ppc64le")
elseif(CLR_CMAKE_HOST_BROWSER)
set(CLR_CMAKE_HOST_ARCH_WASM 1)
set(CLR_CMAKE_HOST_ARCH "wasm")
@@ -303,6 +308,8 @@ if (CLR_CMAKE_TARGET_ARCH STREQUAL x64)
set(ARM_SOFTFP 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL s390x)
set(CLR_CMAKE_TARGET_ARCH_S390X 1)
+ elseif(CLR_CMAKE_TARGET_ARCH STREQUAL ppc64le)
+ set(CLR_CMAKE_TARGET_ARCH_POWERPC64 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL wasm)
set(CLR_CMAKE_TARGET_ARCH_WASM 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL mips64)
@@ -413,6 +420,8 @@ if(CLR_CMAKE_TARGET_UNIX)
set(CLR_CMAKE_TARGET_UNIX_X86 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL s390x)
set(CLR_CMAKE_TARGET_UNIX_S390X 1)
+ elseif(CLR_CMAKE_TARGET_ARCH STREQUAL ppc64le)
+ set(CLR_CMAKE_TARGET_UNIX_POWERPC64 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL wasm)
set(CLR_CMAKE_TARGET_UNIX_WASM 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL mips64)
diff --git a/eng/native/configuretools.cmake b/eng/native/configuretools.cmake
index 3437ce7cdae64a..6697524c6596ae 100644
--- a/eng/native/configuretools.cmake
+++ b/eng/native/configuretools.cmake
@@ -53,7 +53,7 @@ if(NOT WIN32 AND NOT CLR_CMAKE_TARGET_BROWSER)
if(CLR_CMAKE_TARGET_ANDROID)
set(TOOLSET_PREFIX ${ANDROID_TOOLCHAIN_PREFIX})
elseif(CMAKE_CROSSCOMPILING AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD AND
- CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv8l|armv7l|armv6l|aarch64|arm|s390x)$")
+ CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv8l|armv7l|armv6l|aarch64|arm|s390x|ppc64le)$")
set(TOOLSET_PREFIX "${TOOLCHAIN}-")
else()
set(TOOLSET_PREFIX "")
diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake
index eaef0d65df0664..6a8c8f24e9a79c 100644
--- a/eng/native/functions.cmake
+++ b/eng/native/functions.cmake
@@ -175,6 +175,10 @@ function(find_unwind_libs UnwindLibs)
find_library(UNWIND_ARCH NAMES unwind-s390x)
endif()
+ if(CLR_CMAKE_HOST_ARCH_POWERPC64)
+ find_library(UNWIND_ARCH NAMES unwind-ppc64le)
+ endif()
+
if(NOT UNWIND_ARCH STREQUAL UNWIND_ARCH-NOTFOUND)
set(UNWIND_LIBS ${UNWIND_ARCH})
endif()
diff --git a/eng/native/init-os-and-arch.sh b/eng/native/init-os-and-arch.sh
index 7a5815081ad2a8..ded32e3f755f36 100644
--- a/eng/native/init-os-and-arch.sh
+++ b/eng/native/init-os-and-arch.sh
@@ -66,6 +66,9 @@ case "$CPUName" in
arch=s390x
;;
+ ppc64le)
+ arch=ppc64le
+ ;;
*)
echo "Unknown CPU $CPUName detected, configuring as if for x64"
arch=x64
diff --git a/eng/native/tryrun.cmake b/eng/native/tryrun.cmake
index fca410fcb4b42f..c491422ae7bcec 100644
--- a/eng/native/tryrun.cmake
+++ b/eng/native/tryrun.cmake
@@ -68,7 +68,7 @@ if(DARWIN)
else()
message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only arm64 or x64 is supported for OSX cross build!")
endif()
-elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|s390x|x86)$" OR FREEBSD OR ILLUMOS)
+elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|s390x|ppc64le|x86)$" OR FREEBSD OR ILLUMOS)
set_cache_value(FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE 1)
set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 0)
set_cache_value(HAS_POSIX_SEMAPHORES_EXITCODE 0)
@@ -146,9 +146,9 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|s390x|x86)$
set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0)
endif()
else()
- message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, armv6, arm64, loongarch64, s390x and x86 are supported!")
+ message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, armv6, arm64, loongarch64, s390x, ppc64le and x86 are supported!")
endif()
-if(TARGET_ARCH_NAME STREQUAL "x86" OR TARGET_ARCH_NAME STREQUAL "s390x" OR TARGET_ARCH_NAME STREQUAL "armv6" OR TARGET_ARCH_NAME STREQUAL "loongarch64")
+if(TARGET_ARCH_NAME STREQUAL "x86" OR TARGET_ARCH_NAME STREQUAL "s390x" OR TARGET_ARCH_NAME STREQUAL "armv6" OR TARGET_ARCH_NAME STREQUAL "loongarch64" OR TARGET_ARCH_NAME STREQUAL "ppc64le")
set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0)
endif()
diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml
index 9e75af0ac8f6bf..d977f6c692efa1 100644
--- a/eng/pipelines/common/templates/runtimes/run-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml
@@ -227,13 +227,13 @@ jobs:
timeoutInMinutes: 300
${{ else }}:
timeoutInMinutes: 200
- ${{ if in(parameters.testGroup, 'outerloop', 'jit-experimental', 'pgo', 'jit-cfg') }}:
+ ${{ if in(parameters.testGroup, 'outerloop', 'jit-experimental', 'jit-cfg') }}:
timeoutInMinutes: 270
${{ if in(parameters.testGroup, 'gc-longrunning', 'gc-simulator') }}:
timeoutInMinutes: 480
${{ if in(parameters.testGroup, 'jitstress', 'jitstress-isas-arm', 'jitstressregs-x86', 'jitstressregs', 'jitstress2-jitstressregs', 'gcstress0x3-gcstress0xc', 'ilasm') }}:
timeoutInMinutes: 390
- ${{ if in(parameters.testGroup, 'gcstress-extra', 'r2r-extra', 'clrinterpreter') }}:
+ ${{ if in(parameters.testGroup, 'gcstress-extra', 'r2r-extra', 'clrinterpreter', 'pgo') }}:
timeoutInMinutes: 510
${{ if eq(parameters.testGroup, 'jitstress-isas-x86') }}:
timeoutInMinutes: 960
@@ -397,7 +397,7 @@ jobs:
${{ if eq(parameters.runtimeFlavor, 'mono') }}:
# tiered compilation isn't done on mono yet
scenarios:
- - normal
+ - normal
${{ elseif eq(variables['Build.Reason'], 'PullRequest') }}:
scenarios:
- no_tiered_compilation
@@ -545,7 +545,9 @@ jobs:
- defaultpgo
- dynamicpgo
- fullpgo
+ - fullpgo_methodprofiling
- fullpgo_random_gdv
+ - fullpgo_random_gdv_methodprofiling_only
- fullpgo_random_edge
- fullpgo_random_gdv_edge
${{ if in(parameters.testGroup, 'gc-longrunning') }}:
@@ -568,7 +570,6 @@ jobs:
- jitelthookenabled_tiered
${{ if in(parameters.testGroup, 'jit-experimental') }}:
scenarios:
- - jitosr
- jitosr_stress
- jitosr_pgo
- jitosr_stress_random
diff --git a/eng/pipelines/common/templates/wasm-library-aot-tests.yml b/eng/pipelines/common/templates/wasm-library-aot-tests.yml
index 982002ce90520b..170be62247ca4c 100644
--- a/eng/pipelines/common/templates/wasm-library-aot-tests.yml
+++ b/eng/pipelines/common/templates/wasm-library-aot-tests.yml
@@ -8,6 +8,7 @@ parameters:
platforms: []
runAOT: false
runSmokeOnlyArg: ''
+ shouldContinueOnError: false
jobs:
@@ -23,3 +24,4 @@ jobs:
extraHelixArgs: /p:NeedsToBuildWasmAppsOnHelix=true ${{ parameters.extraHelixArgs }}
alwaysRun: ${{ parameters.alwaysRun }}
runSmokeOnlyArg: $(_runSmokeTestsOnlyArg)
+ shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
diff --git a/eng/pipelines/common/templates/wasm-library-tests.yml b/eng/pipelines/common/templates/wasm-library-tests.yml
index 8ebf5646fc7e56..365ea7386d4004 100644
--- a/eng/pipelines/common/templates/wasm-library-tests.yml
+++ b/eng/pipelines/common/templates/wasm-library-tests.yml
@@ -7,6 +7,7 @@ parameters:
platforms: []
runSmokeOnlyArg: ''
scenarios: ['normal']
+ shouldContinueOnError: false
jobs:
@@ -20,6 +21,7 @@ jobs:
buildConfig: Release
runtimeFlavor: mono
platforms: ${{ parameters.platforms }}
+ shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
variables:
# map dependencies variables to local variables
- name: librariesContainsChange
diff --git a/eng/pipelines/coreclr/libraries-pgo.yml b/eng/pipelines/coreclr/libraries-pgo.yml
index 0914451b55ec66..0a3346141744f9 100644
--- a/eng/pipelines/coreclr/libraries-pgo.yml
+++ b/eng/pipelines/coreclr/libraries-pgo.yml
@@ -47,7 +47,7 @@ jobs:
helixQueueGroup: libraries
helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
jobParameters:
- timeoutInMinutes: 150
+ timeoutInMinutes: 600
testScope: innerloop
liveRuntimeBuildConfig: checked
dependsOnTestBuildConfiguration: Release
diff --git a/eng/pipelines/coreclr/templates/build-job.yml b/eng/pipelines/coreclr/templates/build-job.yml
index 74e6b3cfaf0f01..fd077f2e85558b 100644
--- a/eng/pipelines/coreclr/templates/build-job.yml
+++ b/eng/pipelines/coreclr/templates/build-job.yml
@@ -224,6 +224,8 @@ jobs:
# Run CoreCLR Tools unit tests
- ${{ if eq(parameters.testGroup, 'clrTools') }}:
+ - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -subset libs $(crossArg) -arch $(archType) $(osArg) -c $(buildConfig) $(officialBuildIdArg) -ci
+ displayName: Build libs
- script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -subset clr.toolstests $(crossArg) -arch $(archType) $(osArg) -c $(buildConfig) $(officialBuildIdArg) -ci -test
displayName: Run CoreCLR Tools unit tests
diff --git a/eng/pipelines/libraries/run-test-job.yml b/eng/pipelines/libraries/run-test-job.yml
index 17e3a63875d176..c6fbe831a4d9bd 100644
--- a/eng/pipelines/libraries/run-test-job.yml
+++ b/eng/pipelines/libraries/run-test-job.yml
@@ -173,10 +173,11 @@ jobs:
- defaultpgo
- dynamicpgo
- fullpgo
+ - fullpgo_methodprofiling
- fullpgo_random_gdv
+ - fullpgo_random_gdv_methodprofiling_only
- fullpgo_random_edge
- fullpgo_random_gdv_edge
- - jitosr
- jitosr_stress
- jitosr_stress_random
- jitosr_pgo
diff --git a/eng/pipelines/runtime-extra-platforms-other.yml b/eng/pipelines/runtime-extra-platforms-other.yml
index dd8a6e0da07b16..53acaa8c3ee186 100644
--- a/eng/pipelines/runtime-extra-platforms-other.yml
+++ b/eng/pipelines/runtime-extra-platforms-other.yml
@@ -72,6 +72,35 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(variables['isRollingBuild'], true))
+#
+# CoreCLR NativeAOT release build and additional libraries tests that are known to be passing
+# Only when CoreCLR or library is changed
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ platforms:
+ - windows_x64
+ - windows_arm64
+ jobParameters:
+ testGroup: innerloop
+ isSingleFile: true
+ nameSuffix: NativeAOT_Libs_Passing
+ buildArgs: -s clr.alljits+clr.tools+clr.nativeaotlibs+clr.nativeaotruntime+libs+libs.tests -c $(_BuildConfig) /p:TestNativeAot=true /p:ArchiveTests=true
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig)
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(variables['isRollingBuild'], true))
+
# Run net48 tests on win-x64
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
@@ -197,7 +226,7 @@ jobs:
jobParameters:
testGroup: innerloop
nameSuffix: AllSubsets_Mono
- buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:UsePortableRuntimePack=true /p:BuildDarwinFrameworks=true
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=true /p:BuildDarwinFrameworks=true
timeoutInMinutes: 180
condition: >-
or(
@@ -361,7 +390,7 @@ jobs:
jobParameters:
testGroup: innerloop
nameSuffix: AllSubsets_Mono
- buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg)
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:EnableAdditionalTimezoneChecks=true
timeoutInMinutes: 180
condition: >-
or(
diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml
index 35cc083b840b7c..bc75954dfe0963 100644
--- a/eng/pipelines/runtime-staging.yml
+++ b/eng/pipelines/runtime-staging.yml
@@ -97,6 +97,8 @@ jobs:
platforms:
- Browser_wasm_firefox
browser: firefox
+ # ff tests are unstable currently
+ shouldContinueOnError: true
alwaysRun: ${{ variables.isRollingBuild }}
#
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 6e588b348712f9..301809c16cb373 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -260,8 +260,8 @@ jobs:
testGroup: innerloop
isSingleFile: true
nameSuffix: NativeAOT
- buildArgs: -s clr.alljits+clr.tools+clr.nativeaotlibs+clr.nativeaotruntime+libs+libs.tests -c $(_BuildConfig) /p:TestNativeAot=true /p:ArchiveTests=true
- timeoutInMinutes: 120
+ buildArgs: -s clr.alljits+clr.tools+clr.nativeaotlibs+clr.nativeaotruntime+libs+libs.tests -c $(_BuildConfig) /p:TestNativeAot=true /p:RunSmokeTestsOnly=true /p:ArchiveTests=true
+ timeoutInMinutes: 180
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
extraStepsParameters:
@@ -433,7 +433,7 @@ jobs:
jobParameters:
testGroup: innerloop
nameSuffix: AllSubsets_Mono
- buildArgs: -s mono+libs+libs.tests+host+packs -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:RunSmokeTestsOnly=true /p:BuildTestsOnHelix=true /p:UsePortableRuntimePack=true /p:BuildDarwinFrameworks=true
+ buildArgs: -s mono+libs+libs.tests+host+packs -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:RunSmokeTestsOnly=true /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=true /p:BuildDarwinFrameworks=true
timeoutInMinutes: 180
condition: >-
or(
diff --git a/eng/testing/tests.props b/eng/testing/tests.props
index e91ec1bbcf0e38..7ed95b94326e30 100644
--- a/eng/testing/tests.props
+++ b/eng/testing/tests.props
@@ -10,18 +10,6 @@
true
-
-
- <_withCategories Condition="'$(WithCategories)' != ''">;$(WithCategories.Trim(';'))
- <_withoutCategories Condition="'$(WithoutCategories)' != ''">;$(WithoutCategories.Trim(';'))
-
- all
- <_withCategories Condition="'$(TestScope)' == 'outerloop'">$(_withCategories);OuterLoop
- <_withoutCategories Condition="'$(ArchiveTests)' == 'true'">$(_withoutCategories);IgnoreForCI
- <_withoutCategories Condition="'$(TestScope)' == '' or '$(TestScope)' == 'innerloop'">$(_withoutCategories);OuterLoop
- <_withoutCategories Condition="!$(_withCategories.Contains('failing'))">$(_withoutCategories);failing
-
-
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AppleTestRunner', '$(Configuration)', '$(NetCoreAppCurrent)'))
diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets
index df308f3182d449..c358ff594f3dbb 100644
--- a/eng/testing/tests.targets
+++ b/eng/testing/tests.targets
@@ -36,6 +36,18 @@
$(RunScriptHostDir)dotnet
+
+
+ <_withCategories Condition="'$(WithCategories)' != ''">;$(WithCategories.Trim(';'))
+ <_withoutCategories Condition="'$(WithoutCategories)' != ''">;$(WithoutCategories.Trim(';'))
+
+ all
+ <_withCategories Condition="'$(TestScope)' == 'outerloop'">$(_withCategories);OuterLoop
+ <_withoutCategories Condition="'$(ArchiveTests)' == 'true'">$(_withoutCategories);IgnoreForCI
+ <_withoutCategories Condition="'$(TestScope)' == '' or '$(TestScope)' == 'innerloop'">$(_withoutCategories);OuterLoop
+ <_withoutCategories Condition="!$(_withCategories.Contains('failing'))">$(_withoutCategories);failing
+
+
<_MonoAotCrossCompilerPath>$([MSBuild]::NormalizePath($(MonoAotCrossDir), 'mono-aot-cross'))
diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets
index 7f5cdb654efe6f..4c5310cb696f94 100644
--- a/eng/testing/tests.wasm.targets
+++ b/eng/testing/tests.wasm.targets
@@ -5,6 +5,10 @@
true
$(BundleTestAppTargets);BundleTestWasmApp
true
+
+ true
+
+ $(NoWarn);IL2118
$([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk'))
@@ -299,4 +303,8 @@
+
+
+
+
diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets
index 77d7363a22f4ee..75d73d1037442d 100644
--- a/eng/testing/xunit/xunit.console.targets
+++ b/eng/testing/xunit/xunit.console.targets
@@ -36,6 +36,11 @@
$(_withoutCategories.Replace(';', '%0dcategory='))
+
+
+ $(RunScriptCommand) -xml $(TestResultsName)
+
+
(T[] incoming, int requiredSize)
private int m_length;
private byte[] m_ILStream;
- private int[]? m_labelList;
+ private __LabelInfo[]? m_labelList;
private int m_labelCount;
private __FixupData[]? m_fixupData;
@@ -61,10 +61,13 @@ internal static T[] EnlargeArray(T[] incoming, int requiredSize)
internal int m_localCount;
internal SignatureHelper m_localSignature;
- private int m_maxStackSize; // Maximum stack size not counting the exceptions.
+ private int m_curDepth; // Current stack depth, with -1 meaning unknown.
+ private int m_targetDepth; // Stack depth at a target of the previous instruction (when it is branching).
+ private int m_maxDepth; // Running max of the stack depth.
- private int m_maxMidStack; // Maximum stack size for a given basic block.
- private int m_maxMidStackCur; // Running count of the maximum stack size for the current basic block.
+ // Adjustment to add to m_maxDepth for incorrect/invalid IL. For example, when branch instructions
+ // with different stack depths target the same label.
+ private long m_depthAdjustment;
internal int CurrExcStackCount => m_currExcStackCount;
@@ -131,28 +134,32 @@ internal void UpdateStackSize(OpCode opcode, int stackchange)
// requirements for the function. stackchange specifies the amount
// by which the stacksize needs to be updated.
- // Special case for the Return. Returns pops 1 if there is a
- // non-void return value.
-
- // Update the running stacksize. m_maxMidStack specifies the maximum
- // amount of stack required for the current basic block irrespective of
- // where you enter the block.
- m_maxMidStackCur += stackchange;
- if (m_maxMidStackCur > m_maxMidStack)
- m_maxMidStack = m_maxMidStackCur;
- else if (m_maxMidStackCur < 0)
- m_maxMidStackCur = 0;
-
- // If the current instruction signifies end of a basic, which basically
- // means an unconditional branch, add m_maxMidStack to m_maxStackSize.
- // m_maxStackSize will eventually be the sum of the stack requirements for
- // each basic block.
- if (opcode.EndsUncondJmpBlk())
+ if (m_curDepth < 0)
+ {
+ // Current depth is "unknown". We get here when:
+ // * this is unreachable code.
+ // * the client uses explicit numeric offsets rather than Labels.
+ m_curDepth = 0;
+ }
+
+ m_curDepth += stackchange;
+ if (m_curDepth < 0)
{
- m_maxStackSize += m_maxMidStack;
- m_maxMidStack = 0;
- m_maxMidStackCur = 0;
+ // Stack underflow. Assume our previous depth computation was flawed.
+ m_depthAdjustment -= m_curDepth;
+ m_curDepth = 0;
}
+ else if (m_maxDepth < m_curDepth)
+ m_maxDepth = m_curDepth;
+ Debug.Assert(m_depthAdjustment >= 0);
+ Debug.Assert(m_curDepth >= 0);
+
+ // Record the stack depth at a "target" of this instruction.
+ m_targetDepth = m_curDepth;
+
+ // If the current instruction can't fall through, set the depth to unknown.
+ if (opcode.EndsUncondJmpBlk())
+ m_curDepth = -1;
}
private int GetMethodToken(MethodBase method, Type[]? optionalParameterTypes, bool useMethodDef)
@@ -280,10 +287,11 @@ private int GetLabelPos(Label lbl)
if (index < 0 || index >= m_labelCount || m_labelList is null)
throw new ArgumentException(SR.Argument_BadLabel);
- if (m_labelList[index] < 0)
+ int pos = m_labelList[index].m_pos;
+ if (pos < 0)
throw new ArgumentException(SR.Argument_BadLabelContent);
- return m_labelList[index];
+ return pos;
}
private void AddFixup(Label lbl, int pos, int instSize)
@@ -306,11 +314,30 @@ private void AddFixup(Label lbl, int pos, int instSize)
m_fixupLabel = lbl,
m_fixupInstSize = instSize
};
+
+ int labelIndex = lbl.GetLabelValue();
+ if (labelIndex < 0 || labelIndex >= m_labelCount || m_labelList is null)
+ throw new ArgumentException(SR.Argument_BadLabel);
+
+ int depth = m_labelList[labelIndex].m_depth;
+ int targetDepth = m_targetDepth;
+ Debug.Assert(depth >= -1);
+ Debug.Assert(targetDepth >= -1);
+ if (depth < targetDepth)
+ {
+ // Either unknown depth for this label or this branch location has a larger depth than previously recorded.
+ // In the latter case, the IL is (likely) invalid, but we just compensate for it.
+ if (depth >= 0)
+ m_depthAdjustment += targetDepth - depth;
+ m_labelList[labelIndex].m_depth = targetDepth;
+ }
}
internal int GetMaxStackSize()
{
- return m_maxStackSize;
+ // Limit the computed max stack to 2^16 - 1, since the value is mod`ed by 2^16 by other code.
+ Debug.Assert(m_depthAdjustment >= 0);
+ return (int)Math.Min(ushort.MaxValue, m_maxDepth + m_depthAdjustment);
}
private static void SortExceptions(__ExceptionInfo[] exceptions)
@@ -926,7 +953,7 @@ public virtual Label BeginExceptionBlock()
m_currExcStack = EnlargeArray(m_currExcStack);
}
- Label endLabel = DefineLabel();
+ Label endLabel = DefineLabel(0);
__ExceptionInfo exceptionInfo = new __ExceptionInfo(m_length, endLabel);
// add the exception to the tracking list
@@ -934,6 +961,10 @@ public virtual Label BeginExceptionBlock()
// Make this exception the current active exception
m_currExcStack[m_currExcStackCount++] = exceptionInfo;
+
+ // Stack depth for "try" starts at zero.
+ m_curDepth = 0;
+
return endLabel;
}
@@ -969,7 +1000,7 @@ public virtual void EndExceptionBlock()
// Check if we've already set this label.
// The only reason why we might have set this is if we have a finally block.
- Label label = m_labelList![endLabel.GetLabelValue()] != -1
+ Label label = m_labelList![endLabel.GetLabelValue()].m_pos != -1
? current.m_finallyEndLabel
: endLabel;
@@ -990,9 +1021,12 @@ public virtual void BeginExceptFilterBlock()
Emit(OpCodes.Leave, current.GetEndLabel());
current.MarkFilterAddr(m_length);
+
+ // Stack depth for "filter" starts at one.
+ m_curDepth = 1;
}
- public virtual void BeginCatchBlock(Type exceptionType)
+ public virtual void BeginCatchBlock(Type? exceptionType)
{
// Begins a catch block. Emits a branch instruction to the end of the current exception block.
@@ -1020,6 +1054,9 @@ public virtual void BeginCatchBlock(Type exceptionType)
}
current.MarkCatchAddr(m_length, exceptionType);
+
+ // Stack depth for "catch" starts at one.
+ m_curDepth = 1;
}
public virtual void BeginFaultBlock()
@@ -1034,6 +1071,9 @@ public virtual void BeginFaultBlock()
Emit(OpCodes.Leave, current.GetEndLabel());
current.MarkFaultAddr(m_length);
+
+ // Stack depth for "fault" starts at zero.
+ m_curDepth = 0;
}
public virtual void BeginFinallyBlock()
@@ -1055,7 +1095,7 @@ public virtual void BeginFinallyBlock()
MarkLabel(endLabel);
- Label finallyEndLabel = DefineLabel();
+ Label finallyEndLabel = DefineLabel(0);
current.SetFinallyEndLabel(finallyEndLabel);
// generate leave for try clause
@@ -1063,25 +1103,36 @@ public virtual void BeginFinallyBlock()
if (catchEndAddr == 0)
catchEndAddr = m_length;
current.MarkFinallyAddr(m_length, catchEndAddr);
+
+ // Stack depth for "finally" starts at zero.
+ m_curDepth = 0;
}
#endregion
#region Labels
public virtual Label DefineLabel()
+ {
+ // We don't know the stack depth at the label yet, so set it to -1.
+ return DefineLabel(-1);
+ }
+
+ private Label DefineLabel(int depth)
{
// Declares a new Label. This is just a token and does not yet represent any particular location
// within the stream. In order to set the position of the label within the stream, you must call
// Mark Label.
+ Debug.Assert(depth >= -1);
- // Delay init the lable array in case we dont use it
- m_labelList ??= new int[DefaultLabelArraySize];
+ // Delay init the label array in case we dont use it
+ m_labelList ??= new __LabelInfo[DefaultLabelArraySize];
if (m_labelCount >= m_labelList.Length)
{
m_labelList = EnlargeArray(m_labelList);
}
- m_labelList[m_labelCount] = -1;
+ m_labelList[m_labelCount].m_pos = -1;
+ m_labelList[m_labelCount].m_depth = depth;
return new Label(m_labelCount++);
}
@@ -1098,12 +1149,39 @@ public virtual void MarkLabel(Label loc)
throw new ArgumentException(SR.Argument_InvalidLabel);
}
- if (m_labelList[labelIndex] != -1)
+ if (m_labelList[labelIndex].m_pos != -1)
{
throw new ArgumentException(SR.Argument_RedefinedLabel);
}
- m_labelList[labelIndex] = m_length;
+ m_labelList[labelIndex].m_pos = m_length;
+
+ int depth = m_labelList[labelIndex].m_depth;
+ if (depth < 0)
+ {
+ // Unknown depth for this label, indicating that it hasn't been used yet.
+ // If m_curDepth is unknown, we're in the Backward branch constraint case. See ECMA-335 III.1.7.5.
+ // The m_depthAdjustment field will compensate for violations of this constraint, as we
+ // discover them. That is, here we assume a depth of zero. If a (later) branch to this label
+ // has a positive stack depth, we'll record that as the new depth and add the delta into
+ // m_depthAdjustment.
+ if (m_curDepth < 0)
+ m_curDepth = 0;
+ m_labelList[labelIndex].m_depth = m_curDepth;
+ }
+ else if (depth < m_curDepth)
+ {
+ // A branch location with smaller stack targets this label. In this case, the IL is
+ // invalid, but we just compensate for it.
+ m_depthAdjustment += m_curDepth - depth;
+ m_labelList[labelIndex].m_depth = m_curDepth;
+ }
+ else if (depth > m_curDepth)
+ {
+ // Either the current depth is unknown, or a branch location with larger stack targets
+ // this label, so the IL is invalid. In either case, just adjust the current depth.
+ m_curDepth = depth;
+ }
}
#endregion
@@ -1287,6 +1365,12 @@ public virtual void EndScope()
#endregion
}
+ internal struct __LabelInfo
+ {
+ internal int m_pos; // Position in the il stream, with -1 meaning unknown.
+ internal int m_depth; // Stack depth, with -1 meaning unknown.
+ }
+
internal struct __FixupData
{
internal Label m_fixupLabel;
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs
index 10da169b3e33ff..a5f4fdd8ba7d50 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs
@@ -309,7 +309,7 @@ internal int GetMaxStack()
{
if (m_ilGenerator != null)
{
- return m_ilGenerator.GetMaxStackSize() + ExceptionHandlerCount;
+ return m_ilGenerator.GetMaxStackSize();
}
else
{
@@ -323,8 +323,6 @@ internal int GetMaxStack()
return m_exceptions;
}
- internal int ExceptionHandlerCount => m_exceptions != null ? m_exceptions.Length : 0;
-
internal static int CalculateNumberOfExceptions(__ExceptionInfo[]? excp)
{
int num = 0;
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs
index 6acf77be549c01..8468e8f9529e80 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs
@@ -15,13 +15,16 @@ public MethodInvoker(MethodBase method, Signature signature)
_method = method;
_signature = signature;
-#if USE_NATIVE_INVOKE
- // Always use the native invoke; useful for testing.
- _strategyDetermined = true;
-#elif USE_EMIT_INVOKE
- // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing.
- _invoked = true;
-#endif
+ if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
+ {
+ // Always use the native invoke; useful for testing.
+ _strategyDetermined = true;
+ }
+ else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
+ {
+ // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing.
+ _invoked = true;
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
index c6c20002f2cdcb..fa602e0c1dc390 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
@@ -134,9 +134,10 @@ public override AssemblyName GetName(bool copiedName)
an.RawFlags = GetFlags() | AssemblyNameFlags.PublicKey;
-#pragma warning disable IL3000 // System.Reflection.AssemblyName.CodeBase' always returns an empty string for assemblies embedded in a single-file app.
+#pragma warning disable IL3000, SYSLIB0044 // System.Reflection.AssemblyName.CodeBase' always returns an empty string for assemblies embedded in a single-file app.
+ // AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported.
an.CodeBase = GetCodeBase();
-#pragma warning restore IL3000
+#pragma warning restore IL3000, SYSLIB0044
#pragma warning disable SYSLIB0037 // AssemblyName.HashAlgorithm is obsolete
an.HashAlgorithm = GetHashAlgorithm();
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs
index a921099356ec3d..486aeb72faddbc 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs
@@ -477,7 +477,12 @@ private static CastResult TryGet(nuint source, nuint target)
private static object? ChkCastClassSpecial(void* toTypeHnd, object obj)
{
MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
- Debug.Assert(mt != toTypeHnd, "The check for the trivial cases should be inlined by the JIT");
+
+ // Normally, this case is expected to be handled by JIT inline.
+ // However, with PGO data JIT might decide to check a different type instead
+ // so this one has to be always checked here
+ if (toTypeHnd == mt)
+ goto done;
for (; ; )
{
diff --git a/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs b/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs
index 31c98a5bf254a0..64b547b699fb7f 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs
@@ -24,7 +24,7 @@ public abstract class ValueType
{
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Justification = "Trimmed fields don't make a difference for equality")]
- public unsafe override bool Equals([NotNullWhen(true)] object? obj)
+ public override unsafe bool Equals([NotNullWhen(true)] object? obj)
{
if (null == obj)
{
diff --git a/src/coreclr/binder/assemblybindercommon.cpp b/src/coreclr/binder/assemblybindercommon.cpp
index b3edff82d7cbc5..e23c00dfc25687 100644
--- a/src/coreclr/binder/assemblybindercommon.cpp
+++ b/src/coreclr/binder/assemblybindercommon.cpp
@@ -1180,6 +1180,7 @@ HRESULT AssemblyBinderCommon::BindUsingHostAssemblyResolver(/* in */ INT_PTR pMa
HRESULT AssemblyBinderCommon::BindUsingPEImage(/* in */ AssemblyBinder* pBinder,
/* in */ BINDER_SPACE::AssemblyName *pAssemblyName,
/* in */ PEImage *pPEImage,
+ /* in */ bool excludeAppPaths,
/* [retval] [out] */ Assembly **ppAssembly)
{
HRESULT hr = E_FAIL;
@@ -1208,7 +1209,7 @@ HRESULT AssemblyBinderCommon::BindUsingPEImage(/* in */ AssemblyBinder* pBinder
pAssemblyName,
true, // skipFailureCaching
true, // skipVersionCompatibilityCheck
- false, // excludeAppPaths
+ excludeAppPaths, // excludeAppPaths
&bindResult);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
diff --git a/src/coreclr/binder/customassemblybinder.cpp b/src/coreclr/binder/customassemblybinder.cpp
index 98e0fd25de81b6..02a8d2e918dc79 100644
--- a/src/coreclr/binder/customassemblybinder.cpp
+++ b/src/coreclr/binder/customassemblybinder.cpp
@@ -102,6 +102,7 @@ Exit:;
}
HRESULT CustomAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage,
+ /* in */ bool excludeAppPaths,
/* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly)
{
HRESULT hr = S_OK;
@@ -128,7 +129,7 @@ HRESULT CustomAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage,
IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
}
- hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, &pCoreCLRFoundAssembly);
+ hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly);
if (hr == S_OK)
{
_ASSERTE(pCoreCLRFoundAssembly != NULL);
diff --git a/src/coreclr/binder/defaultassemblybinder.cpp b/src/coreclr/binder/defaultassemblybinder.cpp
index 28157a11244cd0..2461ca4b1397d6 100644
--- a/src/coreclr/binder/defaultassemblybinder.cpp
+++ b/src/coreclr/binder/defaultassemblybinder.cpp
@@ -111,6 +111,7 @@ Exit:;
#if !defined(DACCESS_COMPILE)
HRESULT DefaultAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage,
+ /* in */ bool excludeAppPaths,
/* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly)
{
HRESULT hr = S_OK;
@@ -157,7 +158,7 @@ HRESULT DefaultAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage,
}
}
- hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, &pCoreCLRFoundAssembly);
+ hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly);
if (hr == S_OK)
{
_ASSERTE(pCoreCLRFoundAssembly != NULL);
diff --git a/src/coreclr/binder/inc/assemblybindercommon.hpp b/src/coreclr/binder/inc/assemblybindercommon.hpp
index b3b21587159288..3bd529bd9c0a97 100644
--- a/src/coreclr/binder/inc/assemblybindercommon.hpp
+++ b/src/coreclr/binder/inc/assemblybindercommon.hpp
@@ -56,6 +56,7 @@ namespace BINDER_SPACE
static HRESULT BindUsingPEImage(/* in */ AssemblyBinder *pBinder,
/* in */ BINDER_SPACE::AssemblyName *pAssemblyName,
/* in */ PEImage *pPEImage,
+ /* in */ bool excludeAppPaths,
/* [retval] [out] */ Assembly **ppAssembly);
#endif // !defined(DACCESS_COMPILE)
diff --git a/src/coreclr/binder/inc/customassemblybinder.h b/src/coreclr/binder/inc/customassemblybinder.h
index 9d59832f3c9798..7a89c5033b9e50 100644
--- a/src/coreclr/binder/inc/customassemblybinder.h
+++ b/src/coreclr/binder/inc/customassemblybinder.h
@@ -18,6 +18,7 @@ class CustomAssemblyBinder final : public AssemblyBinder
public:
HRESULT BindUsingPEImage(PEImage* pPEImage,
+ bool excludeAppPaths,
BINDER_SPACE::Assembly** ppAssembly) override;
HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName,
diff --git a/src/coreclr/binder/inc/defaultassemblybinder.h b/src/coreclr/binder/inc/defaultassemblybinder.h
index 398174c65a078b..3d35854e09f3ff 100644
--- a/src/coreclr/binder/inc/defaultassemblybinder.h
+++ b/src/coreclr/binder/inc/defaultassemblybinder.h
@@ -16,6 +16,7 @@ class DefaultAssemblyBinder final : public AssemblyBinder
public:
HRESULT BindUsingPEImage(PEImage* pPEImage,
+ bool excludeAppPaths,
BINDER_SPACE::Assembly** ppAssembly) override;
HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName,
diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp
index 2d517279fad6fa..2f1387ddd2fcfc 100644
--- a/src/coreclr/classlibnative/bcltype/system.cpp
+++ b/src/coreclr/classlibnative/bcltype/system.cpp
@@ -90,43 +90,6 @@ FCIMPL0(INT32, SystemNative::GetExitCode)
}
FCIMPLEND
-FCIMPL0(Object*, SystemNative::GetCommandLineArgs)
-{
- FCALL_CONTRACT;
-
- PTRARRAYREF strArray = NULL;
-
- HELPER_METHOD_FRAME_BEGIN_RET_1(strArray);
-
- LPWSTR commandLine;
-
- commandLine = WszGetCommandLine();
- if (commandLine==NULL)
- COMPlusThrowOM();
-
- DWORD numArgs = 0;
- LPWSTR* argv = SegmentCommandLine(commandLine, &numArgs);
- if (!argv)
- COMPlusThrowOM();
-
- _ASSERTE(numArgs > 0);
-
- strArray = (PTRARRAYREF) AllocateObjectArray(numArgs, g_pStringClass);
- // Copy each argument into new Strings.
- for(unsigned int i=0; iGetDataPtr())) + i;
- SetObjectReference((OBJECTREF*)destData, (OBJECTREF)str);
- }
- delete [] argv;
-
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(strArray);
-}
-FCIMPLEND
-
// Return a method info for the method were the exception was thrown
FCIMPL1(ReflectMethodObject*, SystemNative::GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE)
{
diff --git a/src/coreclr/classlibnative/bcltype/system.h b/src/coreclr/classlibnative/bcltype/system.h
index 27e772be2af07d..b4a773a847c398 100644
--- a/src/coreclr/classlibnative/bcltype/system.h
+++ b/src/coreclr/classlibnative/bcltype/system.h
@@ -43,7 +43,6 @@ class SystemNative
static FCDECL1(VOID,SetExitCode,INT32 exitcode);
static FCDECL0(INT32, GetExitCode);
- static FCDECL0(Object*, GetCommandLineArgs);
static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
static FCDECL2(VOID, FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode);
static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp
index 564aa84cd687eb..55c7a4d0829f95 100644
--- a/src/coreclr/debug/daccess/daccess.cpp
+++ b/src/coreclr/debug/daccess/daccess.cpp
@@ -2501,7 +2501,7 @@ namespace serialization { namespace bin {
return ErrOverflow;
}
- memcpy_s(dest, destSize, s.GetUTF8NoConvert(), cnt);
+ memcpy_s(dest, destSize, s.GetUTF8(), cnt);
return cnt;
}
diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp
index 6ae350448755d5..9fb920f18ed609 100644
--- a/src/coreclr/debug/daccess/dacdbiimpl.cpp
+++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp
@@ -6450,7 +6450,7 @@ HRESULT DacHeapWalker::MoveToNextObject()
bool DacHeapWalker::GetSize(TADDR tMT, size_t &size)
{
- // With heap corruption, it's entierly possible that the MethodTable
+ // With heap corruption, it's entirely possible that the MethodTable
// we get is bad. This could cause exceptions, which we will catch
// and return false. This causes the heapwalker to move to the next
// segment.
@@ -6478,6 +6478,12 @@ bool DacHeapWalker::GetSize(TADDR tMT, size_t &size)
size = AlignLarge(size);
else
size = Align(size);
+
+ // If size == 0, it means we have a heap corruption and
+ // we will stuck in an infinite loop, so better fail the call now.
+ ret &= (0 < size);
+ // Also guard for cases where the size reported is too large and exceeds the high allocation mark.
+ ret &= ((tMT + size) <= mHeaps[mCurrHeap].Segments[mCurrSeg].End);
}
EX_CATCH
{
diff --git a/src/coreclr/debug/daccess/ppc64le/primitives.cpp b/src/coreclr/debug/daccess/ppc64le/primitives.cpp
new file mode 100644
index 00000000000000..6bbe30049c101e
--- /dev/null
+++ b/src/coreclr/debug/daccess/ppc64le/primitives.cpp
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+
+#include "stdafx.h"
+
+#include "../../shared/ppc64le/primitives.cpp"
diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp
index f73b1ce8afa2c1..35aac26d72b222 100644
--- a/src/coreclr/debug/daccess/request.cpp
+++ b/src/coreclr/debug/daccess/request.cpp
@@ -647,9 +647,10 @@ ClrDataAccess::GetRegisterName(int regNum, unsigned int count, _Inout_updates_z_
return E_UNEXPECTED;
const WCHAR callerPrefix[] = W("caller.");
- unsigned int prefixLen = (unsigned int)ARRAY_SIZE(callerPrefix) - 1;
- unsigned int regLen = (unsigned int)wcslen(regs[regNum]);
- unsigned int needed = (callerFrame?prefixLen:0) + regLen + 1;
+ // Include null terminator in prefixLen/regLen because wcscpy_s will fail otherwise
+ unsigned int prefixLen = (unsigned int)ARRAY_SIZE(callerPrefix);
+ unsigned int regLen = (unsigned int)wcslen(regs[regNum]) + 1;
+ unsigned int needed = (callerFrame ? prefixLen - 1 : 0) + regLen;
if (pNeeded)
*pNeeded = needed;
@@ -662,6 +663,8 @@ ClrDataAccess::GetRegisterName(int regNum, unsigned int count, _Inout_updates_z_
{
unsigned int toCopy = prefixLen < destSize ? prefixLen : destSize;
wcscpy_s(curr, toCopy, callerPrefix);
+ // Point to null terminator
+ toCopy--;
curr += toCopy;
destSize -= toCopy;
}
@@ -670,6 +673,8 @@ ClrDataAccess::GetRegisterName(int regNum, unsigned int count, _Inout_updates_z_
{
unsigned int toCopy = regLen < destSize ? regLen : destSize;
wcscpy_s(curr, toCopy, regs[regNum]);
+ // Point to null terminator
+ toCopy--;
curr += toCopy;
destSize -= toCopy;
}
diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp
index 032e96457ecc1b..954836a3e0f746 100644
--- a/src/coreclr/debug/ee/debugger.cpp
+++ b/src/coreclr/debug/ee/debugger.cpp
@@ -1010,7 +1010,7 @@ MemoryRange Debugger::s_hijackFunction[kMaxHijackFunctions] =
RedirectedHandledJITCaseForDbgThreadControl_StubEnd),
GetMemoryRangeForFunction(RedirectedHandledJITCaseForUserSuspend_Stub,
RedirectedHandledJITCaseForUserSuspend_StubEnd)
-#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64)
+#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) && defined(USE_REDIRECT_FOR_GCSTRESS)
,
GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCStress_Stub,
RedirectedHandledJITCaseForGCStress_StubEnd)
diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h
index 39b203fa589234..a5c5ecd0fd620c 100644
--- a/src/coreclr/debug/ee/debugger.h
+++ b/src/coreclr/debug/ee/debugger.h
@@ -2938,7 +2938,7 @@ void RedirectedHandledJITCaseForDbgThreadControl_StubEnd();
void RedirectedHandledJITCaseForUserSuspend_Stub();
void RedirectedHandledJITCaseForUserSuspend_StubEnd();
-#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64)
+#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) && defined(USE_REDIRECT_FOR_GCSTRESS)
void RedirectedHandledJITCaseForGCStress_Stub();
void RedirectedHandledJITCaseForGCStress_StubEnd();
#endif // HAVE_GCCOVER && TARGET_AMD64
diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp
index f5f21da3baee8b..2b287eb07fcd17 100644
--- a/src/coreclr/debug/ee/functioninfo.cpp
+++ b/src/coreclr/debug/ee/functioninfo.cpp
@@ -1518,6 +1518,9 @@ DebuggerJitInfo * DebuggerMethodInfo::FindJitInfo(MethodDesc * pMD,
}
CONTRACTL_END;
+#ifdef TARGET_ARM
+ addrNativeStartAddr = addrNativeStartAddr|THUMB_CODE;
+#endif
DebuggerJitInfo * pCheck = m_latestJitInfo;
while (pCheck != NULL)
diff --git a/src/coreclr/debug/ee/ppc64le/dbghelpers.S b/src/coreclr/debug/ee/ppc64le/dbghelpers.S
new file mode 100644
index 00000000000000..a1ec66394511ea
--- /dev/null
+++ b/src/coreclr/debug/ee/ppc64le/dbghelpers.S
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "asmconstants.h"
+#include "unixasmmacros.inc"
+
+#error Unsupported platform
+
diff --git a/src/coreclr/debug/ee/ppc64le/primitives.cpp b/src/coreclr/debug/ee/ppc64le/primitives.cpp
new file mode 100644
index 00000000000000..9b2e216d091ca2
--- /dev/null
+++ b/src/coreclr/debug/ee/ppc64le/primitives.cpp
@@ -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.
+
+//
+
+#include "stdafx.h"
+#include "threads.h"
+#include "../../shared/ppc64le/primitives.cpp"
+
diff --git a/src/coreclr/debug/shared/ppc64le/primitives.cpp b/src/coreclr/debug/shared/ppc64le/primitives.cpp
new file mode 100644
index 00000000000000..cb4be30c89b56e
--- /dev/null
+++ b/src/coreclr/debug/shared/ppc64le/primitives.cpp
@@ -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.
+//*****************************************************************************
+// File: primitives.cpp
+//
+
+//
+// Platform-specific debugger primitives
+//
+//*****************************************************************************
+
+#include "primitives.h"
+
+#error Unsupported platform
+
diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc
index f6dc44d8b06ee3..6a96d6c03e0ea1 100644
--- a/src/coreclr/dlls/mscorrc/mscorrc.rc
+++ b/src/coreclr/dlls/mscorrc/mscorrc.rc
@@ -415,7 +415,6 @@ BEGIN
IDS_EE_SIZECONTROLBADTYPE "Array size control parameter type not supported."
IDS_EE_SAFEARRAYSZARRAYMISMATCH "SafeArray cannot be marshaled to this array type because it has either nonzero lower bounds or more than one dimension."
- IDS_EE_ASSEMBLY_GETTYPE_CANNONT_HAVE_ASSEMBLY_SPEC "Type names passed to Assembly.GetType() must not specify an assembly."
IDS_EE_NEEDS_ASSEMBLY_SPEC "Typename needs an assembly qualifier."
IDS_EE_FILELOAD_ERROR_GENERIC "Could not load file or assembly '%1'. %2"
diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h
index ed503b4875ad60..351ff690aaca65 100644
--- a/src/coreclr/dlls/mscorrc/resource.h
+++ b/src/coreclr/dlls/mscorrc/resource.h
@@ -305,7 +305,6 @@
#define IDS_CLASSLOAD_OVERLAPPING_INTERFACES 0x1a80
#define IDS_CLASSLOAD_32BITCLRLOADING64BITASSEMBLY 0x1a81
-#define IDS_EE_ASSEMBLY_GETTYPE_CANNONT_HAVE_ASSEMBLY_SPEC 0x1a84
#define IDS_EE_NEEDS_ASSEMBLY_SPEC 0x1a87
diff --git a/src/coreclr/gc/env/etmdummy.h b/src/coreclr/gc/env/etmdummy.h
index 556372127577a9..575e3067cf89a1 100644
--- a/src/coreclr/gc/env/etmdummy.h
+++ b/src/coreclr/gc/env/etmdummy.h
@@ -78,6 +78,7 @@
#define FireEtwThreadPoolWorkerThreadAdjustmentAdjustment(AverageThroughput, NewWorkerThreadCount, Reason, ClrInstanceID) 0
#define FireEtwThreadPoolWorkerThreadAdjustmentStats(Duration, Throughput, ThreadWave, ThroughputWave, ThroughputErrorEstimate, AverageThroughputErrorEstimate, ThroughputRatio, Confidence, NewControlSetting, NewThreadWaveMagnitude, ClrInstanceID) 0
#define FireEtwThreadPoolWorkerThreadWait(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID) 0
+#define FireEtwThreadPoolMinMaxThreads (MinWorkerThreads, MaxWorkerThreads, MinIOCompletionThreads, MaxIOCompletionThreads, ClrInstanceID) 0
#define FireEtwThreadPoolWorkingThreadCount(Count, ClrInstanceID) 0
#define FireEtwThreadPoolEnqueue(WorkID, ClrInstanceID) 0
#define FireEtwThreadPoolDequeue(WorkID, ClrInstanceID) 0
diff --git a/src/coreclr/gc/env/gcenv.os.h b/src/coreclr/gc/env/gcenv.os.h
index c37f62509e8bc3..25a15e3847d340 100644
--- a/src/coreclr/gc/env/gcenv.os.h
+++ b/src/coreclr/gc/env/gcenv.os.h
@@ -6,17 +6,6 @@
#ifndef __GCENV_OS_H__
#define __GCENV_OS_H__
-#ifdef Sleep
-// This is a funny workaround for the fact that "common.h" defines Sleep to be
-// Dont_Use_Sleep, with the hope of causing linker errors whenever someone tries to use sleep.
-//
-// However, GCToOSInterface defines a function called Sleep, which (due to this define) becomes
-// "Dont_Use_Sleep", which the GC in turn happily uses. The symbol that GCToOSInterface actually
-// exported was called "GCToOSInterface::Dont_Use_Sleep". While we progress in making the GC standalone,
-// we'll need to break the dependency on common.h (the VM header) and this problem will become moot.
-#undef Sleep
-#endif // Sleep
-
#ifdef HAS_SYSTEM_YIELDPROCESSOR
// YieldProcessor is defined to Dont_Use_YieldProcessor. Restore it to the system-default implementation for the GC.
#undef YieldProcessor
diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp
index ec96a2e6fb65db..1021d669b2dc36 100644
--- a/src/coreclr/gc/gc.cpp
+++ b/src/coreclr/gc/gc.cpp
@@ -44526,9 +44526,25 @@ Object * GCHeap::NextObj (Object * object)
return NULL;
}
- if ((nextobj < heap_segment_mem(hs)) ||
- (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) ||
- (nextobj >= hp->alloc_allocated))
+ if (nextobj < heap_segment_mem (hs))
+ {
+ return NULL;
+ }
+
+ uint8_t* saved_alloc_allocated = hp->alloc_allocated;
+ heap_segment* saved_ephemeral_heap_segment = hp->ephemeral_heap_segment;
+
+ // We still want to verify nextobj that lands between heap_segment_allocated and alloc_allocated
+ // on the ephemeral segment. In regions these 2 could be changed by another thread so we need
+ // to make sure they are still in sync by the time we check. If they are not in sync, we just
+ // bail which means we don't validate the next object during that small window and that's fine.
+ //
+ // We also miss validating nextobj if it's in the segment that just turned into the new ephemeral
+ // segment since we saved which is also a very small window and again that's fine.
+ if ((nextobj >= heap_segment_allocated (hs)) &&
+ ((hs != saved_ephemeral_heap_segment) ||
+ !in_range_for_segment(saved_alloc_allocated, saved_ephemeral_heap_segment) ||
+ (nextobj >= saved_alloc_allocated)))
{
return NULL;
}
diff --git a/src/coreclr/gc/unix/cgroup.cpp b/src/coreclr/gc/unix/cgroup.cpp
index 34f7f4f47f62df..50069e469147e2 100644
--- a/src/coreclr/gc/unix/cgroup.cpp
+++ b/src/coreclr/gc/unix/cgroup.cpp
@@ -47,6 +47,10 @@ Module Name:
#define CGROUP1_MEMORY_LIMIT_FILENAME "/memory.limit_in_bytes"
#define CGROUP2_MEMORY_LIMIT_FILENAME "/memory.max"
#define CGROUP_MEMORY_STAT_FILENAME "/memory.stat"
+#define CGROUP1_MEMORY_USAGE_FILENAME "/memory.usage_in_bytes"
+#define CGROUP2_MEMORY_USAGE_FILENAME "/memory.current"
+#define CGROUP1_MEMORY_STAT_INACTIVE_FIELD "total_inactive_file "
+#define CGROUP2_MEMORY_STAT_INACTIVE_FIELD "inactive_file "
extern bool ReadMemoryValueFromFile(const char* filename, uint64_t* val);
@@ -56,36 +60,11 @@ class CGroup
static int s_cgroup_version;
static char *s_memory_cgroup_path;
-
- static const char *s_mem_stat_key_names[];
- static size_t s_mem_stat_key_lengths[];
- static size_t s_mem_stat_n_keys;
public:
static void Initialize()
{
s_cgroup_version = FindCGroupVersion();
s_memory_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1MemorySubsystem : nullptr);
-
- if (s_cgroup_version == 1)
- {
- s_mem_stat_n_keys = 4;
- s_mem_stat_key_names[0] = "total_inactive_anon ";
- s_mem_stat_key_names[1] = "total_active_anon ";
- s_mem_stat_key_names[2] = "total_dirty ";
- s_mem_stat_key_names[3] = "total_unevictable ";
- }
- else
- {
- s_mem_stat_n_keys = 3;
- s_mem_stat_key_names[0] = "anon ";
- s_mem_stat_key_names[1] = "file_dirty ";
- s_mem_stat_key_names[2] = "unevictable ";
- }
-
- for (size_t i = 0; i < s_mem_stat_n_keys; i++)
- {
- s_mem_stat_key_lengths[i] = strlen(s_mem_stat_key_names[i]);
- }
}
static void Cleanup()
@@ -113,9 +92,9 @@ class CGroup
if (s_cgroup_version == 0)
return false;
else if (s_cgroup_version == 1)
- return GetCGroupMemoryUsage(val);
+ return GetCGroupMemoryUsage(val, CGROUP1_MEMORY_USAGE_FILENAME, CGROUP1_MEMORY_STAT_INACTIVE_FIELD);
else if (s_cgroup_version == 2)
- return GetCGroupMemoryUsage(val);
+ return GetCGroupMemoryUsage(val, CGROUP2_MEMORY_USAGE_FILENAME, CGROUP2_MEMORY_STAT_INACTIVE_FIELD);
else
{
assert(!"Unknown cgroup version.");
@@ -401,8 +380,38 @@ class CGroup
return result;
}
- static bool GetCGroupMemoryUsage(size_t *val)
+ static bool GetCGroupMemoryUsage(size_t *val, const char *filename, const char *inactiveFileFieldName)
{
+ // Use the same way to calculate memory load as popular container tools (Docker, Kubernetes, Containerd etc.)
+ // For cgroup v1: value of 'memory.usage_in_bytes' minus 'total_inactive_file' value of 'memory.stat'
+ // For cgroup v2: value of 'memory.current' minus 'inactive_file' value of 'memory.stat'
+
+ char* mem_usage_filename = nullptr;
+ if (asprintf(&mem_usage_filename, "%s%s", s_memory_cgroup_path, filename) < 0)
+ return false;
+
+ uint64_t temp = 0;
+
+ size_t usage = 0;
+
+ bool result = ReadMemoryValueFromFile(mem_usage_filename, &temp);
+ if (result)
+ {
+ if (temp > std::numeric_limits::max())
+ {
+ usage = std::numeric_limits::max();
+ }
+ else
+ {
+ usage = (size_t)temp;
+ }
+ }
+
+ free(mem_usage_filename);
+
+ if (!result)
+ return result;
+
if (s_memory_cgroup_path == nullptr)
return false;
@@ -417,44 +426,38 @@ class CGroup
char *line = nullptr;
size_t lineLen = 0;
- size_t readValues = 0;
+ bool foundInactiveFileValue = false;
char* endptr;
- *val = 0;
- while (getline(&line, &lineLen, stat_file) != -1 && readValues < s_mem_stat_n_keys)
+ size_t inactiveFileFieldNameLength = strlen(inactiveFileFieldName);
+
+ while (getline(&line, &lineLen, stat_file) != -1)
{
- for (size_t i = 0; i < s_mem_stat_n_keys; i++)
+ if (strncmp(line, inactiveFileFieldName, inactiveFileFieldNameLength) == 0)
{
- if (strncmp(line, s_mem_stat_key_names[i], s_mem_stat_key_lengths[i]) == 0)
+ errno = 0;
+ const char* startptr = line + inactiveFileFieldNameLength;
+ size_t inactiveFileValue = strtoll(startptr, &endptr, 10);
+ if (endptr != startptr && errno == 0)
{
- errno = 0;
- const char* startptr = line + s_mem_stat_key_lengths[i];
- *val += strtoll(startptr, &endptr, 10);
- if (endptr != startptr && errno == 0)
- readValues++;
-
- break;
+ foundInactiveFileValue = true;
+ *val = usage - inactiveFileValue;
}
+
+ break;
}
}
fclose(stat_file);
free(line);
- if (readValues == s_mem_stat_n_keys)
- return true;
-
- return false;
+ return foundInactiveFileValue;
}
};
int CGroup::s_cgroup_version = 0;
char *CGroup::s_memory_cgroup_path = nullptr;
-const char *CGroup::s_mem_stat_key_names[4] = {};
-size_t CGroup::s_mem_stat_key_lengths[4] = {};
-size_t CGroup::s_mem_stat_n_keys = 0;
-
void InitializeCGroup()
{
CGroup::Initialize();
diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp
index 8b536ad9dd7d82..4ad5f4b3795e80 100644
--- a/src/coreclr/gc/unix/gcenv.unix.cpp
+++ b/src/coreclr/gc/unix/gcenv.unix.cpp
@@ -876,21 +876,29 @@ bool ReadMemoryValueFromFile(const char* filename, uint64_t* val)
return result;
}
+#define UPDATE_CACHE_SIZE_AND_LEVEL(CACHE_LEVEL) if (size > cacheSize) { cacheSize = size; cacheLevel = CACHE_LEVEL; }
+
static size_t GetLogicalProcessorCacheSizeFromOS()
{
+ size_t cacheLevel = 0;
size_t cacheSize = 0;
+ size_t size;
#ifdef _SC_LEVEL1_DCACHE_SIZE
- cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL1_DCACHE_SIZE));
+ size = ( size_t) sysconf(_SC_LEVEL1_DCACHE_SIZE);
+ UPDATE_CACHE_SIZE_AND_LEVEL(1)
#endif
#ifdef _SC_LEVEL2_CACHE_SIZE
- cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL2_CACHE_SIZE));
+ size = ( size_t) sysconf(_SC_LEVEL2_CACHE_SIZE);
+ UPDATE_CACHE_SIZE_AND_LEVEL(2)
#endif
#ifdef _SC_LEVEL3_CACHE_SIZE
- cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL3_CACHE_SIZE));
+ size = ( size_t) sysconf(_SC_LEVEL3_CACHE_SIZE);
+ UPDATE_CACHE_SIZE_AND_LEVEL(3)
#endif
#ifdef _SC_LEVEL4_CACHE_SIZE
- cacheSize = std::max(cacheSize, ( size_t) sysconf(_SC_LEVEL4_CACHE_SIZE));
+ size = ( size_t) sysconf(_SC_LEVEL4_CACHE_SIZE);
+ UPDATE_CACHE_SIZE_AND_LEVEL(4)
#endif
#if defined(TARGET_LINUX) && !defined(HOST_ARM) && !defined(HOST_X86)
@@ -901,25 +909,39 @@ static size_t GetLogicalProcessorCacheSizeFromOS()
// for the platform. Currently musl and arm64 should be only cases to use
// this method to determine cache size.
//
- size_t size;
-
- if (ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index0/size", &size))
- cacheSize = std::max(cacheSize, size);
- if (ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index1/size", &size))
- cacheSize = std::max(cacheSize, size);
- if (ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index2/size", &size))
- cacheSize = std::max(cacheSize, size);
- if (ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index3/size", &size))
- cacheSize = std::max(cacheSize, size);
- if (ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index4/size", &size))
- cacheSize = std::max(cacheSize, size);
+ size_t level;
+ char path_to_size_file[] = "/sys/devices/system/cpu/cpu0/cache/index-/size";
+ char path_to_level_file[] = "/sys/devices/system/cpu/cpu0/cache/index-/level";
+ int index = 40;
+ assert(path_to_size_file[index] == '-');
+ assert(path_to_level_file[index] == '-');
+
+ for (int i = 0; i < 5; i++)
+ {
+ path_to_size_file[index] = (char)(48 + i);
+
+ if (ReadMemoryValueFromFile(path_to_size_file, &size))
+ {
+ path_to_level_file[index] = (char)(48 + i);
+
+ if (ReadMemoryValueFromFile(path_to_level_file, &level))
+ {
+ UPDATE_CACHE_SIZE_AND_LEVEL(level)
+ }
+ else
+ {
+ cacheSize = std::max(cacheSize, size);
+ }
+ }
+ }
}
#endif
-#if defined(HOST_ARM64) && !defined(TARGET_OSX)
+#if (defined(HOST_ARM64) || defined(HOST_LOONGARCH64)) && !defined(TARGET_OSX)
if (cacheSize == 0)
{
- // It is currently expected to be missing cache size info
+ // We expect to get the L3 cache size for Arm64 but currently expected to be missing that info
+ // from most of the machines.
//
// _SC_LEVEL*_*CACHE_SIZE is not yet present. Work is in progress to enable this for arm64
//
@@ -964,6 +986,38 @@ static size_t GetLogicalProcessorCacheSizeFromOS()
}
#endif
+#if (defined(HOST_ARM64) || defined(HOST_LOONGARCH64)) && !defined(TARGET_OSX)
+ if (cacheLevel != 3)
+ {
+ // We expect to get the L3 cache size for Arm64 but currently expected to be missing that info
+ // from most of the machines.
+ // Hence, just use the following heuristics at best depending on the CPU count
+ // 1 ~ 4 : 4 MB
+ // 5 ~ 16 : 8 MB
+ // 17 ~ 64 : 16 MB
+ // 65+ : 32 MB
+ DWORD logicalCPUs = g_totalCpuCount;
+ if (logicalCPUs < 5)
+ {
+ cacheSize = 4;
+ }
+ else if (logicalCPUs < 17)
+ {
+ cacheSize = 8;
+ }
+ else if (logicalCPUs < 65)
+ {
+ cacheSize = 16;
+ }
+ else
+ {
+ cacheSize = 32;
+ }
+
+ cacheSize *= (1024 * 1024);
+ }
+#endif
+
return cacheSize;
}
@@ -1037,15 +1091,10 @@ size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
size_t maxSize, maxTrueSize;
maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS(); // Returns the size of the highest level processor cache
-#if defined(HOST_ARM64)
- // Bigger gen0 size helps arm64 targets
- maxSize = maxTrueSize * 3;
-#endif
-
s_maxSize = maxSize;
s_maxTrueSize = maxTrueSize;
- // printf("GetCacheSizePerLogicalCpu returns %d, adjusted size %d\n", maxSize, maxTrueSize);
+ // printf("GetCacheSizePerLogicalCpu returns %zu, adjusted size %zu\n", maxSize, maxTrueSize);
return trueSize ? maxTrueSize : maxSize;
}
diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp
index 8bb35badd6c2fa..ca7cc4132e5eee 100644
--- a/src/coreclr/gc/windows/gcenv.windows.cpp
+++ b/src/coreclr/gc/windows/gcenv.windows.cpp
@@ -402,6 +402,8 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION *GetLPI(PDWORD nEntries)
size_t GetLogicalProcessorCacheSizeFromOS()
{
size_t cache_size = 0;
+ size_t cache_level = 0;
+
DWORD nEntries = 0;
// Try to use GetLogicalProcessorInformation API and get a valid pointer to the SLPI array if successful. Returns NULL
@@ -424,7 +426,11 @@ size_t GetLogicalProcessorCacheSizeFromOS()
{
if (pslpi[i].Relationship == RelationCache)
{
- last_cache_size = max(last_cache_size, pslpi[i].Cache.Size);
+ if (last_cache_size < pslpi[i].Cache.Size)
+ {
+ last_cache_size = pslpi[i].Cache.Size;
+ cache_level = pslpi[i].Cache.Level;
+ }
}
}
cache_size = last_cache_size;
@@ -434,6 +440,39 @@ size_t GetLogicalProcessorCacheSizeFromOS()
if(pslpi)
delete[] pslpi; // release the memory allocated for the SLPI array.
+#if defined(TARGET_ARM64)
+ if (cache_level != 3)
+ {
+ uint32_t totalCPUCount = GCToOSInterface::GetTotalProcessorCount();
+
+ // We expect to get the L3 cache size for Arm64 but currently expected to be missing that info
+ // from most of the machines.
+ // Hence, just use the following heuristics at best depending on the CPU count
+ // 1 ~ 4 : 4 MB
+ // 5 ~ 16 : 8 MB
+ // 17 ~ 64 : 16 MB
+ // 65+ : 32 MB
+ if (totalCPUCount < 5)
+ {
+ cache_size = 4;
+ }
+ else if (totalCPUCount < 17)
+ {
+ cache_size = 8;
+ }
+ else if (totalCPUCount < 65)
+ {
+ cache_size = 16;
+ }
+ else
+ {
+ cache_size = 32;
+ }
+
+ cache_size *= (1024 * 1024);
+ }
+#endif // TARGET_ARM64
+
return cache_size;
}
@@ -836,15 +875,10 @@ size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
-#if defined(TARGET_ARM64)
- // Bigger gen0 size helps arm64 targets
- maxSize = maxTrueSize * 3;
-#endif
-
s_maxSize = maxSize;
s_maxTrueSize = maxTrueSize;
- // printf("GetCacheSizePerLogicalCpu returns %d, adjusted size %d\n", maxSize, maxTrueSize);
+ // printf("GetCacheSizePerLogicalCpu returns %zu, adjusted size %zu\n", maxSize, maxTrueSize);
return trueSize ? maxTrueSize : maxSize;
}
diff --git a/src/coreclr/ilasm/assembler.h b/src/coreclr/ilasm/assembler.h
index 76f6787021fd37..43ecddf2ea0b54 100644
--- a/src/coreclr/ilasm/assembler.h
+++ b/src/coreclr/ilasm/assembler.h
@@ -53,10 +53,6 @@
#define dwUniBuf 16384
-#ifdef TARGET_UNIX
-extern char *g_pszExeFile;
-#endif
-
extern WCHAR wzUniBuf[]; // Unicode conversion global buffer (assem.cpp)
class Class;
diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp
index 146e8640b3cb47..d6d6e9201b1bfe 100644
--- a/src/coreclr/ilasm/main.cpp
+++ b/src/coreclr/ilasm/main.cpp
@@ -29,9 +29,6 @@ static DWORD g_dwSubsystem=(DWORD)-1,g_dwComImageFlags=(DWORD)-1,g_dwFileAlig
static ULONGLONG g_stBaseAddress=0;
static size_t g_stSizeOfStackReserve=0;
extern unsigned int g_uConsoleCP;
-#ifdef TARGET_UNIX
-char * g_pszExeFile;
-#endif
void MakeTestFile(_In_ __nullterminated char* szFileName)
{
@@ -855,7 +852,6 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv)
#ifdef TARGET_UNIX
int main(int argc, char* str[])
{
- g_pszExeFile = str[0];
if (0 != PAL_Initialize(argc, str))
{
fprintf(stderr,"Error: Fail to PAL_Initialize\n");
diff --git a/src/coreclr/ildasm/dasm.cpp b/src/coreclr/ildasm/dasm.cpp
index 03999e3c61872b..9942397f9da6d2 100644
--- a/src/coreclr/ildasm/dasm.cpp
+++ b/src/coreclr/ildasm/dasm.cpp
@@ -123,7 +123,6 @@ BOOL g_fCustomInstructionEncodingSystem = FALSE;
COR_FIELD_OFFSET *g_rFieldOffset = NULL;
ULONG g_cFieldsMax, g_cFieldOffsets;
-char* g_pszExeFile;
char g_szInputFile[MAX_FILENAME_LENGTH]; // in UTF-8
WCHAR g_wszFullInputFile[MAX_PATH + 1]; // in UTF-16
char g_szOutputFile[MAX_FILENAME_LENGTH]; // in UTF-8
diff --git a/src/coreclr/ildasm/windasm.cpp b/src/coreclr/ildasm/windasm.cpp
index 6ef983bc583c5a..bf80bad26b6cf0 100644
--- a/src/coreclr/ildasm/windasm.cpp
+++ b/src/coreclr/ildasm/windasm.cpp
@@ -55,7 +55,6 @@ extern char g_szAsmCodeIndent[];
extern DWORD g_Mode;
-extern char* g_pszExeFile;
extern char g_szInputFile[]; // in UTF-8
extern WCHAR g_wszFullInputFile[]; // in UTF-16
extern char g_szOutputFile[]; // in UTF-8
@@ -424,37 +423,10 @@ int ProcessOneArg(_In_ __nullterminated char* szArg, _Out_ char** ppszObjFileNam
return 0;
}
-char* UTF8toANSI(_In_ __nullterminated char* szUTF)
-{
- ULONG32 L = (ULONG32) strlen(szUTF)+16;
- WCHAR* wzUnicode = new WCHAR[L];
- memset(wzUnicode,0,L*sizeof(WCHAR));
- WszMultiByteToWideChar(CP_UTF8,0,szUTF,-1,wzUnicode,L);
- L <<= 2;
- char* szANSI = new char[L];
- memset(szANSI,0,L);
- WszWideCharToMultiByte(g_uConsoleCP,0,wzUnicode,-1,szANSI,L,NULL,NULL);
- VDELETE(wzUnicode);
- return szANSI;
-}
-char* ANSItoUTF8(_In_ __nullterminated char* szANSI)
-{
- ULONG32 L = (ULONG32) strlen(szANSI)+16;
- WCHAR* wzUnicode = new WCHAR[L];
- memset(wzUnicode,0,L*sizeof(WCHAR));
- WszMultiByteToWideChar(g_uConsoleCP,0,szANSI,-1,wzUnicode,L);
- L *= 3;
- char* szUTF = new char[L];
- memset(szUTF,0,L);
- WszWideCharToMultiByte(CP_UTF8,0,wzUnicode,-1,szUTF,L,NULL,NULL);
- VDELETE(wzUnicode);
- return szUTF;
-}
-
-int ParseCmdLineW(_In_ __nullterminated WCHAR* wzCmdLine, _Out_ char** ppszObjFileName)
+#ifdef HOST_WINDOWS
+int ParseCmdLineW(int argc, _In_ __nullterminated wchar_t* argv[], _Out_ char** ppszObjFileName)
{
- int argc,ret=0;
- LPWSTR* argv= SegmentCommandLine(wzCmdLine, (DWORD*)&argc);
+ int ret=0;
char* szArg = new char[2048];
for(int i=1; i < argc; i++)
{
@@ -465,54 +437,32 @@ int ParseCmdLineW(_In_ __nullterminated WCHAR* wzCmdLine, _Out_ char** ppszObjFi
VDELETE(szArg);
return ret;
}
-
-int ParseCmdLineA(_In_ __nullterminated char* szCmdLine, _Out_ char** ppszObjFileName)
+#else
+int ParseCmdLine(int argc, _In_ __nullterminated char* argv[], _Out_ char** ppszObjFileName)
{
- if((szCmdLine == NULL)||(*szCmdLine == 0)) return 0;
-
- // ANSI to UTF-8
- char* szCmdLineUTF = ANSItoUTF8(szCmdLine);
-
- // Split into argv[]
- int argc=0, ret = 0;
- DynamicArray argv;
- char* pch;
- char* pchend;
- bool bUnquoted = true;
-
- pch = szCmdLineUTF;
- pchend = pch+strlen(szCmdLineUTF);
- while(pch)
+ int ret = 0;
+ char* szArg = new char[2048];
+ for (int i = 1; i < argc; i++)
{
- for(; *pch == ' '; pch++); // skip the blanks
- argv[argc++] = pch;
- for(; pch < pchend; pch++)
- {
- if(*pch == '"') bUnquoted = !bUnquoted;
- else if((*pch == ' ')&&bUnquoted) break;
- }
-
- if(pch < pchend) *pch++ = 0;
- else break;
+ if ((ret = ProcessOneArg(argv[i], ppszObjFileName)) != 0) break;
}
-
- for(int i=1; i < argc; i++)
- {
- if((ret = ProcessOneArg(argv[i],ppszObjFileName)) != 0) break;
- }
- VDELETE(szCmdLineUTF);
+ VDELETE(szArg);
return ret;
}
+#endif
-int __cdecl main(int nCmdShow, char* lpCmdLine[])
+#ifdef HOST_WINDOWS
+int __cdecl wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
{
#if defined(TARGET_UNIX)
- if (0 != PAL_Initialize(nCmdShow, lpCmdLine))
+ if (0 != PAL_Initialize(argc, argv))
{
printError(g_pFile, "Error: Fail to PAL_Initialize\n");
exit(1);
}
- g_pszExeFile = lpCmdLine[0];
#endif
#ifdef HOST_WINDOWS
@@ -552,7 +502,12 @@ int __cdecl main(int nCmdShow, char* lpCmdLine[])
g_hResources = WszGetModuleHandle(NULL);
#endif
- iCommandLineParsed = ParseCmdLineW((wzCommandLine = GetCommandLineW()),&g_pszObjFileName);
+
+#ifdef HOST_WINDOWS
+ iCommandLineParsed = ParseCmdLineW(argc, argv, &g_pszObjFileName);
+#else
+ iCommandLineParsed = ParseCmdLine(argc, argv, &g_pszObjFileName);
+#endif
if(!g_fLimitedVisibility)
{
diff --git a/src/coreclr/inc/contxt.h b/src/coreclr/inc/contxt.h
deleted file mode 100644
index 1611e7616f5d3c..00000000000000
--- a/src/coreclr/inc/contxt.h
+++ /dev/null
@@ -1,3471 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/* this ALWAYS GENERATED file contains the definitions for the interfaces */
-
-
-/* File created by MIDL compiler version 5.01.0164 */
-/* at Mon May 01 14:39:38 2000
- */
-/* Compiler settings for contxt.idl:
- Os (OptLev=s), W1, Zp8, env=Win32, ms_ext, c_ext
- error checks: allocation ref bounds_check enum stub_data
-*/
-//@@MIDL_FILE_HEADING( )
-
-
-/* verify that the version is high enough to compile this file*/
-#ifndef __REQUIRED_RPCNDR_H_VERSION__
-#define __REQUIRED_RPCNDR_H_VERSION__ 440
-#endif
-
-#include "rpc.h"
-#include "rpcndr.h"
-
-#ifndef __RPCNDR_H_VERSION__
-#error this stub requires an updated version of
-#endif // __RPCNDR_H_VERSION__
-
-#ifndef COM_NO_WINDOWS_H
-#include "windows.h"
-#include "ole2.h"
-#endif /*COM_NO_WINDOWS_H*/
-
-#ifndef __contxt_h__
-#define __contxt_h__
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-/* Forward Declarations */
-
-#ifndef __IEnumContextProps_FWD_DEFINED__
-#define __IEnumContextProps_FWD_DEFINED__
-typedef interface IEnumContextProps IEnumContextProps;
-#endif /* __IEnumContextProps_FWD_DEFINED__ */
-
-
-#ifndef __IContext_FWD_DEFINED__
-#define __IContext_FWD_DEFINED__
-typedef interface IContext IContext;
-#endif /* __IContext_FWD_DEFINED__ */
-
-
-#ifndef __IContextMarshaler_FWD_DEFINED__
-#define __IContextMarshaler_FWD_DEFINED__
-typedef interface IContextMarshaler IContextMarshaler;
-#endif /* __IContextMarshaler_FWD_DEFINED__ */
-
-
-#ifndef __IObjContext_FWD_DEFINED__
-#define __IObjContext_FWD_DEFINED__
-typedef interface IObjContext IObjContext;
-#endif /* __IObjContext_FWD_DEFINED__ */
-
-
-#ifndef __IGetContextId_FWD_DEFINED__
-#define __IGetContextId_FWD_DEFINED__
-typedef interface IGetContextId IGetContextId;
-#endif /* __IGetContextId_FWD_DEFINED__ */
-
-
-#ifndef __IAggregator_FWD_DEFINED__
-#define __IAggregator_FWD_DEFINED__
-typedef interface IAggregator IAggregator;
-#endif /* __IAggregator_FWD_DEFINED__ */
-
-
-#ifndef __ICall_FWD_DEFINED__
-#define __ICall_FWD_DEFINED__
-typedef interface ICall ICall;
-#endif /* __ICall_FWD_DEFINED__ */
-
-
-#ifndef __IRpcCall_FWD_DEFINED__
-#define __IRpcCall_FWD_DEFINED__
-typedef interface IRpcCall IRpcCall;
-#endif /* __IRpcCall_FWD_DEFINED__ */
-
-
-#ifndef __ICallInfo_FWD_DEFINED__
-#define __ICallInfo_FWD_DEFINED__
-typedef interface ICallInfo ICallInfo;
-#endif /* __ICallInfo_FWD_DEFINED__ */
-
-
-#ifndef __IPolicy_FWD_DEFINED__
-#define __IPolicy_FWD_DEFINED__
-typedef interface IPolicy IPolicy;
-#endif /* __IPolicy_FWD_DEFINED__ */
-
-
-#ifndef __IPolicyAsync_FWD_DEFINED__
-#define __IPolicyAsync_FWD_DEFINED__
-typedef interface IPolicyAsync IPolicyAsync;
-#endif /* __IPolicyAsync_FWD_DEFINED__ */
-
-
-#ifndef __IPolicySet_FWD_DEFINED__
-#define __IPolicySet_FWD_DEFINED__
-typedef interface IPolicySet IPolicySet;
-#endif /* __IPolicySet_FWD_DEFINED__ */
-
-
-#ifndef __IComObjIdentity_FWD_DEFINED__
-#define __IComObjIdentity_FWD_DEFINED__
-typedef interface IComObjIdentity IComObjIdentity;
-#endif /* __IComObjIdentity_FWD_DEFINED__ */
-
-
-#ifndef __IPolicyMaker_FWD_DEFINED__
-#define __IPolicyMaker_FWD_DEFINED__
-typedef interface IPolicyMaker IPolicyMaker;
-#endif /* __IPolicyMaker_FWD_DEFINED__ */
-
-
-#ifndef __IExceptionNotification_FWD_DEFINED__
-#define __IExceptionNotification_FWD_DEFINED__
-typedef interface IExceptionNotification IExceptionNotification;
-#endif /* __IExceptionNotification_FWD_DEFINED__ */
-
-
-#ifndef __IMarshalEnvoy_FWD_DEFINED__
-#define __IMarshalEnvoy_FWD_DEFINED__
-typedef interface IMarshalEnvoy IMarshalEnvoy;
-#endif /* __IMarshalEnvoy_FWD_DEFINED__ */
-
-
-#ifndef __IWrapperInfo_FWD_DEFINED__
-#define __IWrapperInfo_FWD_DEFINED__
-typedef interface IWrapperInfo IWrapperInfo;
-#endif /* __IWrapperInfo_FWD_DEFINED__ */
-
-
-#ifndef __IComThreadingInfo_FWD_DEFINED__
-#define __IComThreadingInfo_FWD_DEFINED__
-typedef interface IComThreadingInfo IComThreadingInfo;
-#endif /* __IComThreadingInfo_FWD_DEFINED__ */
-
-
-#ifndef __IComDispatchInfo_FWD_DEFINED__
-#define __IComDispatchInfo_FWD_DEFINED__
-typedef interface IComDispatchInfo IComDispatchInfo;
-#endif /* __IComDispatchInfo_FWD_DEFINED__ */
-
-
-/* header files for imported files */
-#include "wtypes.h"
-#include "objidl.h"
-
-void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
-void __RPC_USER MIDL_user_free( void __RPC_FAR * );
-
-/* interface __MIDL_itf_contxt_0000 */
-/* [local] */
-
-enum tagCONTEXTEVENT
- { CONTEXTEVENT_NONE = 0,
- CONTEXTEVENT_CALL = 0x1,
- CONTEXTEVENT_ENTER = 0x2,
- CONTEXTEVENT_LEAVE = 0x4,
- CONTEXTEVENT_RETURN = 0x8,
- CONTEXTEVENT_CALLFILLBUFFER = 0x10,
- CONTEXTEVENT_ENTERWITHBUFFER = 0x20,
- CONTEXTEVENT_LEAVEFILLBUFFER = 0x40,
- CONTEXTEVENT_RETURNWITHBUFFER = 0x80,
- CONTEXTEVENT_BEGINCALL = 0x100,
- CONTEXTEVENT_BEGINENTER = 0x200,
- CONTEXTEVENT_BEGINLEAVE = 0x400,
- CONTEXTEVENT_BEGINRETURN = 0x800,
- CONTEXTEVENT_FINISHCALL = 0x1000,
- CONTEXTEVENT_FINISHENTER = 0x2000,
- CONTEXTEVENT_FINISHLEAVE = 0x4000,
- CONTEXTEVENT_FINISHRETURN = 0x8000,
- CONTEXTEVENT_BEGINCALLFILLBUFFER = 0x10000,
- CONTEXTEVENT_BEGINENTERWITHBUFFER = 0x20000,
- CONTEXTEVENT_FINISHLEAVEFILLBUFFER = 0x40000,
- CONTEXTEVENT_FINISHRETURNWITHBUFFER = 0x80000,
- CONTEXTEVENT_LEAVEEXCEPTION = 0x100000,
- CONTEXTEVENT_LEAVEEXCEPTIONFILLBUFFER = 0x200000,
- CONTEXTEVENT_RETURNEXCEPTION = 0x400000,
- CONTEXTEVENT_RETURNEXCEPTIONWITHBUFFER = 0x800000,
- CONTEXTEVENT_ADDREFPOLICY = 0x10000000,
- CONTEXTEVENT_RELEASEPOLICY = 0x20000000
- };
-typedef DWORD ContextEvent;
-
-
-enum tagCPFLAGS
- { CPFLAG_NONE = 0,
- CPFLAG_PROPAGATE = 0x1,
- CPFLAG_EXPOSE = 0x2,
- CPFLAG_ENVOY = 0x4,
- CPFLAG_MONITORSTUB = 0x8,
- CPFLAG_MONITORPROXY = 0x10,
- CPFLAG_DONTCOMPARE = 0x20
- };
-typedef DWORD CPFLAGS;
-
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0000_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0000_v0_0_s_ifspec;
-
-#ifndef __IEnumContextProps_INTERFACE_DEFINED__
-#define __IEnumContextProps_INTERFACE_DEFINED__
-
-/* interface IEnumContextProps */
-/* [unique][uuid][object] */
-
-typedef /* [unique] */ IEnumContextProps __RPC_FAR *LPENUMCONTEXTPROPS;
-
-
-EXTERN_C const IID IID_IEnumContextProps;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c1-0000-0000-C000-000000000046")
- IEnumContextProps : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Next(
- /* [in] */ ULONG celt,
- /* [length_is][size_is][out] */ ContextProperty __RPC_FAR *pContextProperties,
- /* [out] */ ULONG __RPC_FAR *pceltFetched) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Skip(
- /* [in] */ ULONG celt) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Clone(
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Count(
- /* [out] */ ULONG __RPC_FAR *pcelt) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IEnumContextPropsVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IEnumContextProps __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IEnumContextProps __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IEnumContextProps __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )(
- IEnumContextProps __RPC_FAR * This,
- /* [in] */ ULONG celt,
- /* [length_is][size_is][out] */ ContextProperty __RPC_FAR *pContextProperties,
- /* [out] */ ULONG __RPC_FAR *pceltFetched);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )(
- IEnumContextProps __RPC_FAR * This,
- /* [in] */ ULONG celt);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )(
- IEnumContextProps __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )(
- IEnumContextProps __RPC_FAR * This,
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Count )(
- IEnumContextProps __RPC_FAR * This,
- /* [out] */ ULONG __RPC_FAR *pcelt);
-
- END_INTERFACE
- } IEnumContextPropsVtbl;
-
- interface IEnumContextProps
- {
- CONST_VTBL struct IEnumContextPropsVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IEnumContextProps_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IEnumContextProps_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IEnumContextProps_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IEnumContextProps_Next(This,celt,pContextProperties,pceltFetched) \
- (This)->lpVtbl -> Next(This,celt,pContextProperties,pceltFetched)
-
-#define IEnumContextProps_Skip(This,celt) \
- (This)->lpVtbl -> Skip(This,celt)
-
-#define IEnumContextProps_Reset(This) \
- (This)->lpVtbl -> Reset(This)
-
-#define IEnumContextProps_Clone(This,ppEnumContextProps) \
- (This)->lpVtbl -> Clone(This,ppEnumContextProps)
-
-#define IEnumContextProps_Count(This,pcelt) \
- (This)->lpVtbl -> Count(This,pcelt)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IEnumContextProps_Next_Proxy(
- IEnumContextProps __RPC_FAR * This,
- /* [in] */ ULONG celt,
- /* [length_is][size_is][out] */ ContextProperty __RPC_FAR *pContextProperties,
- /* [out] */ ULONG __RPC_FAR *pceltFetched);
-
-
-void __RPC_STUB IEnumContextProps_Next_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IEnumContextProps_Skip_Proxy(
- IEnumContextProps __RPC_FAR * This,
- /* [in] */ ULONG celt);
-
-
-void __RPC_STUB IEnumContextProps_Skip_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IEnumContextProps_Reset_Proxy(
- IEnumContextProps __RPC_FAR * This);
-
-
-void __RPC_STUB IEnumContextProps_Reset_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IEnumContextProps_Clone_Proxy(
- IEnumContextProps __RPC_FAR * This,
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps);
-
-
-void __RPC_STUB IEnumContextProps_Clone_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IEnumContextProps_Count_Proxy(
- IEnumContextProps __RPC_FAR * This,
- /* [out] */ ULONG __RPC_FAR *pcelt);
-
-
-void __RPC_STUB IEnumContextProps_Count_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IEnumContextProps_INTERFACE_DEFINED__ */
-
-
-#ifndef __IContext_INTERFACE_DEFINED__
-#define __IContext_INTERFACE_DEFINED__
-
-/* interface IContext */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IContext;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c0-0000-0000-C000-000000000046")
- IContext : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetProperty(
- /* [in] */ REFGUID rpolicyId,
- /* [in] */ CPFLAGS flags,
- /* [in] */ IUnknown __RPC_FAR *pUnk) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE RemoveProperty(
- /* [in] */ REFGUID rPolicyId) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetProperty(
- /* [in] */ REFGUID rGuid,
- /* [out] */ CPFLAGS __RPC_FAR *pFlags,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE EnumContextProps(
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IContextVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IContext __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IContext __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IContext __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProperty )(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rpolicyId,
- /* [in] */ CPFLAGS flags,
- /* [in] */ IUnknown __RPC_FAR *pUnk);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RemoveProperty )(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rPolicyId);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperty )(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rGuid,
- /* [out] */ CPFLAGS __RPC_FAR *pFlags,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumContextProps )(
- IContext __RPC_FAR * This,
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps);
-
- END_INTERFACE
- } IContextVtbl;
-
- interface IContext
- {
- CONST_VTBL struct IContextVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IContext_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IContext_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IContext_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IContext_SetProperty(This,rpolicyId,flags,pUnk) \
- (This)->lpVtbl -> SetProperty(This,rpolicyId,flags,pUnk)
-
-#define IContext_RemoveProperty(This,rPolicyId) \
- (This)->lpVtbl -> RemoveProperty(This,rPolicyId)
-
-#define IContext_GetProperty(This,rGuid,pFlags,ppUnk) \
- (This)->lpVtbl -> GetProperty(This,rGuid,pFlags,ppUnk)
-
-#define IContext_EnumContextProps(This,ppEnumContextProps) \
- (This)->lpVtbl -> EnumContextProps(This,ppEnumContextProps)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IContext_SetProperty_Proxy(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rpolicyId,
- /* [in] */ CPFLAGS flags,
- /* [in] */ IUnknown __RPC_FAR *pUnk);
-
-
-void __RPC_STUB IContext_SetProperty_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IContext_RemoveProperty_Proxy(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rPolicyId);
-
-
-void __RPC_STUB IContext_RemoveProperty_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IContext_GetProperty_Proxy(
- IContext __RPC_FAR * This,
- /* [in] */ REFGUID rGuid,
- /* [out] */ CPFLAGS __RPC_FAR *pFlags,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk);
-
-
-void __RPC_STUB IContext_GetProperty_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IContext_EnumContextProps_Proxy(
- IContext __RPC_FAR * This,
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps);
-
-
-void __RPC_STUB IContext_EnumContextProps_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IContext_INTERFACE_DEFINED__ */
-
-
-#ifndef __IContextMarshaler_INTERFACE_DEFINED__
-#define __IContextMarshaler_INTERFACE_DEFINED__
-
-/* interface IContextMarshaler */
-/* [uuid][object][local] */
-
-typedef /* [unique] */ IContextMarshaler __RPC_FAR *LPCTXMARSHALER;
-
-
-EXTERN_C const IID IID_IContextMarshaler;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001D8-0000-0000-C000-000000000046")
- IContextMarshaler : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetMarshalSizeMax(
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags,
- /* [out] */ DWORD __RPC_FAR *pSize) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE MarshalInterface(
- /* [unique][in] */ IStream __RPC_FAR *pStm,
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IContextMarshalerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IContextMarshaler __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IContextMarshaler __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IContextMarshaler __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetMarshalSizeMax )(
- IContextMarshaler __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags,
- /* [out] */ DWORD __RPC_FAR *pSize);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MarshalInterface )(
- IContextMarshaler __RPC_FAR * This,
- /* [unique][in] */ IStream __RPC_FAR *pStm,
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags);
-
- END_INTERFACE
- } IContextMarshalerVtbl;
-
- interface IContextMarshaler
- {
- CONST_VTBL struct IContextMarshalerVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IContextMarshaler_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IContextMarshaler_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IContextMarshaler_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IContextMarshaler_GetMarshalSizeMax(This,riid,pv,dwDestContext,pvDestContext,mshlflags,pSize) \
- (This)->lpVtbl -> GetMarshalSizeMax(This,riid,pv,dwDestContext,pvDestContext,mshlflags,pSize)
-
-#define IContextMarshaler_MarshalInterface(This,pStm,riid,pv,dwDestContext,pvDestContext,mshlflags) \
- (This)->lpVtbl -> MarshalInterface(This,pStm,riid,pv,dwDestContext,pvDestContext,mshlflags)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IContextMarshaler_GetMarshalSizeMax_Proxy(
- IContextMarshaler __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags,
- /* [out] */ DWORD __RPC_FAR *pSize);
-
-
-void __RPC_STUB IContextMarshaler_GetMarshalSizeMax_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IContextMarshaler_MarshalInterface_Proxy(
- IContextMarshaler __RPC_FAR * This,
- /* [unique][in] */ IStream __RPC_FAR *pStm,
- /* [in] */ REFIID riid,
- /* [unique][in] */ void __RPC_FAR *pv,
- /* [in] */ DWORD dwDestContext,
- /* [unique][in] */ void __RPC_FAR *pvDestContext,
- /* [in] */ DWORD mshlflags);
-
-
-void __RPC_STUB IContextMarshaler_MarshalInterface_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IContextMarshaler_INTERFACE_DEFINED__ */
-
-
-// Placing the following definition here rather than with the IObjContext stuff
-// below is a temporary workaround to get around build problems where the system
-// objidl.h now has a IObjContext section but has not made much public (all the
-// interface methods are marked as reserved and the following typedef does not
-// exist). Once the system objidl.h is updated again we can remove the entire
-// section.
-#ifndef __PFNCTXCALLBACK_HACK
-#define __PFNCTXCALLBACK_HACK
-typedef /* [ref] */ HRESULT ( __stdcall __RPC_FAR *PFNCTXCALLBACK )(
- void __RPC_FAR *pParam);
-#endif
-
-#ifndef __IObjContext_INTERFACE_DEFINED__
-#define __IObjContext_INTERFACE_DEFINED__
-
-/* interface IObjContext */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IObjContext;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c6-0000-0000-C000-000000000046")
- IObjContext : public IContext
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Freeze( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DoCallback(
- /* [in] */ PFNCTXCALLBACK pfnCallback,
- /* [in] */ void __RPC_FAR *pParam,
- /* [in] */ REFIID riid,
- /* [in] */ unsigned int iMethod) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetContextMarshaler(
- /* [in] */ IContextMarshaler __RPC_FAR *pICM) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetContextMarshaler(
- /* [out] */ IContextMarshaler __RPC_FAR *__RPC_FAR *pICM) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetContextFlags(
- /* [in] */ DWORD dwFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ClearContextFlags(
- /* [in] */ DWORD dwFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetContextFlags(
- /* [out] */ DWORD __RPC_FAR *pdwFlags) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IObjContextVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IObjContext __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IObjContext __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IObjContext __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProperty )(
- IObjContext __RPC_FAR * This,
- /* [in] */ REFGUID rpolicyId,
- /* [in] */ CPFLAGS flags,
- /* [in] */ IUnknown __RPC_FAR *pUnk);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RemoveProperty )(
- IObjContext __RPC_FAR * This,
- /* [in] */ REFGUID rPolicyId);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetProperty )(
- IObjContext __RPC_FAR * This,
- /* [in] */ REFGUID rGuid,
- /* [out] */ CPFLAGS __RPC_FAR *pFlags,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumContextProps )(
- IObjContext __RPC_FAR * This,
- /* [out] */ IEnumContextProps __RPC_FAR *__RPC_FAR *ppEnumContextProps);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Freeze )(
- IObjContext __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DoCallback )(
- IObjContext __RPC_FAR * This,
- /* [in] */ PFNCTXCALLBACK pfnCallback,
- /* [in] */ void __RPC_FAR *pParam,
- /* [in] */ REFIID riid,
- /* [in] */ unsigned int iMethod);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetContextMarshaler )(
- IObjContext __RPC_FAR * This,
- /* [in] */ IContextMarshaler __RPC_FAR *pICM);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetContextMarshaler )(
- IObjContext __RPC_FAR * This,
- /* [out] */ IContextMarshaler __RPC_FAR *__RPC_FAR *pICM);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetContextFlags )(
- IObjContext __RPC_FAR * This,
- /* [in] */ DWORD dwFlags);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ClearContextFlags )(
- IObjContext __RPC_FAR * This,
- /* [in] */ DWORD dwFlags);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetContextFlags )(
- IObjContext __RPC_FAR * This,
- /* [out] */ DWORD __RPC_FAR *pdwFlags);
-
- END_INTERFACE
- } IObjContextVtbl;
-
- interface IObjContext
- {
- CONST_VTBL struct IObjContextVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IObjContext_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IObjContext_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IObjContext_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IObjContext_SetProperty(This,rpolicyId,flags,pUnk) \
- (This)->lpVtbl -> SetProperty(This,rpolicyId,flags,pUnk)
-
-#define IObjContext_RemoveProperty(This,rPolicyId) \
- (This)->lpVtbl -> RemoveProperty(This,rPolicyId)
-
-#define IObjContext_GetProperty(This,rGuid,pFlags,ppUnk) \
- (This)->lpVtbl -> GetProperty(This,rGuid,pFlags,ppUnk)
-
-#define IObjContext_EnumContextProps(This,ppEnumContextProps) \
- (This)->lpVtbl -> EnumContextProps(This,ppEnumContextProps)
-
-
-#define IObjContext_Freeze(This) \
- (This)->lpVtbl -> Freeze(This)
-
-#define IObjContext_DoCallback(This,pfnCallback,pParam,riid,iMethod) \
- (This)->lpVtbl -> DoCallback(This,pfnCallback,pParam,riid,iMethod)
-
-#define IObjContext_SetContextMarshaler(This,pICM) \
- (This)->lpVtbl -> SetContextMarshaler(This,pICM)
-
-#define IObjContext_GetContextMarshaler(This,pICM) \
- (This)->lpVtbl -> GetContextMarshaler(This,pICM)
-
-#define IObjContext_SetContextFlags(This,dwFlags) \
- (This)->lpVtbl -> SetContextFlags(This,dwFlags)
-
-#define IObjContext_ClearContextFlags(This,dwFlags) \
- (This)->lpVtbl -> ClearContextFlags(This,dwFlags)
-
-#define IObjContext_GetContextFlags(This,pdwFlags) \
- (This)->lpVtbl -> GetContextFlags(This,pdwFlags)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_Freeze_Proxy(
- IObjContext __RPC_FAR * This);
-
-
-void __RPC_STUB IObjContext_Freeze_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_DoCallback_Proxy(
- IObjContext __RPC_FAR * This,
- /* [in] */ PFNCTXCALLBACK pfnCallback,
- /* [in] */ void __RPC_FAR *pParam,
- /* [in] */ REFIID riid,
- /* [in] */ unsigned int iMethod);
-
-
-void __RPC_STUB IObjContext_DoCallback_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_SetContextMarshaler_Proxy(
- IObjContext __RPC_FAR * This,
- /* [in] */ IContextMarshaler __RPC_FAR *pICM);
-
-
-void __RPC_STUB IObjContext_SetContextMarshaler_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_GetContextMarshaler_Proxy(
- IObjContext __RPC_FAR * This,
- /* [out] */ IContextMarshaler __RPC_FAR *__RPC_FAR *pICM);
-
-
-void __RPC_STUB IObjContext_GetContextMarshaler_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_SetContextFlags_Proxy(
- IObjContext __RPC_FAR * This,
- /* [in] */ DWORD dwFlags);
-
-
-void __RPC_STUB IObjContext_SetContextFlags_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_ClearContextFlags_Proxy(
- IObjContext __RPC_FAR * This,
- /* [in] */ DWORD dwFlags);
-
-
-void __RPC_STUB IObjContext_ClearContextFlags_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IObjContext_GetContextFlags_Proxy(
- IObjContext __RPC_FAR * This,
- /* [out] */ DWORD __RPC_FAR *pdwFlags);
-
-
-void __RPC_STUB IObjContext_GetContextFlags_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IObjContext_INTERFACE_DEFINED__ */
-
-
-#ifndef __IGetContextId_INTERFACE_DEFINED__
-#define __IGetContextId_INTERFACE_DEFINED__
-
-/* interface IGetContextId */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IGetContextId;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001dd-0000-0000-C000-000000000046")
- IGetContextId : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetContextId(
- /* [out] */ GUID __RPC_FAR *pguidCtxtId) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IGetContextIdVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IGetContextId __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IGetContextId __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IGetContextId __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetContextId )(
- IGetContextId __RPC_FAR * This,
- /* [out] */ GUID __RPC_FAR *pguidCtxtId);
-
- END_INTERFACE
- } IGetContextIdVtbl;
-
- interface IGetContextId
- {
- CONST_VTBL struct IGetContextIdVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IGetContextId_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IGetContextId_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IGetContextId_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IGetContextId_GetContextId(This,pguidCtxtId) \
- (This)->lpVtbl -> GetContextId(This,pguidCtxtId)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IGetContextId_GetContextId_Proxy(
- IGetContextId __RPC_FAR * This,
- /* [out] */ GUID __RPC_FAR *pguidCtxtId);
-
-
-void __RPC_STUB IGetContextId_GetContextId_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IGetContextId_INTERFACE_DEFINED__ */
-
-
-#ifndef __IAggregator_INTERFACE_DEFINED__
-#define __IAggregator_INTERFACE_DEFINED__
-
-/* interface IAggregator */
-/* [unique][uuid][object][local] */
-
-typedef /* [unique] */ IAggregator __RPC_FAR *IAGGREGATOR;
-
-
-EXTERN_C const IID IID_IAggregator;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001d8-0000-0000-C000-000000000046")
- IAggregator : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Aggregate(
- /* [in] */ IUnknown __RPC_FAR *pInnerUnk) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IAggregatorVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IAggregator __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IAggregator __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IAggregator __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Aggregate )(
- IAggregator __RPC_FAR * This,
- /* [in] */ IUnknown __RPC_FAR *pInnerUnk);
-
- END_INTERFACE
- } IAggregatorVtbl;
-
- interface IAggregator
- {
- CONST_VTBL struct IAggregatorVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IAggregator_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IAggregator_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IAggregator_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IAggregator_Aggregate(This,pInnerUnk) \
- (This)->lpVtbl -> Aggregate(This,pInnerUnk)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IAggregator_Aggregate_Proxy(
- IAggregator __RPC_FAR * This,
- /* [in] */ IUnknown __RPC_FAR *pInnerUnk);
-
-
-void __RPC_STUB IAggregator_Aggregate_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IAggregator_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICall_INTERFACE_DEFINED__
-#define __ICall_INTERFACE_DEFINED__
-
-/* interface ICall */
-/* [unique][uuid][object][local] */
-
-typedef /* [unique] */ ICall __RPC_FAR *LPCALL;
-
-
-EXTERN_C const IID IID_ICall;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001d6-0000-0000-C000-000000000046")
- ICall : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetCallInfo(
- /* [out] */ const void __RPC_FAR *__RPC_FAR *ppIdentity,
- /* [out] */ IID __RPC_FAR *piid,
- /* [out] */ DWORD __RPC_FAR *pdwMethod,
- /* [out] */ HRESULT __RPC_FAR *phr) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Nullify(
- /* [in] */ HRESULT hr) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetServerHR(
- /* [out] */ HRESULT __RPC_FAR *phr) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct ICallVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- ICall __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- ICall __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- ICall __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCallInfo )(
- ICall __RPC_FAR * This,
- /* [out] */ const void __RPC_FAR *__RPC_FAR *ppIdentity,
- /* [out] */ IID __RPC_FAR *piid,
- /* [out] */ DWORD __RPC_FAR *pdwMethod,
- /* [out] */ HRESULT __RPC_FAR *phr);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Nullify )(
- ICall __RPC_FAR * This,
- /* [in] */ HRESULT hr);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetServerHR )(
- ICall __RPC_FAR * This,
- /* [out] */ HRESULT __RPC_FAR *phr);
-
- END_INTERFACE
- } ICallVtbl;
-
- interface ICall
- {
- CONST_VTBL struct ICallVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICall_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define ICall_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define ICall_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define ICall_GetCallInfo(This,ppIdentity,piid,pdwMethod,phr) \
- (This)->lpVtbl -> GetCallInfo(This,ppIdentity,piid,pdwMethod,phr)
-
-#define ICall_Nullify(This,hr) \
- (This)->lpVtbl -> Nullify(This,hr)
-
-#define ICall_GetServerHR(This,phr) \
- (This)->lpVtbl -> GetServerHR(This,phr)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE ICall_GetCallInfo_Proxy(
- ICall __RPC_FAR * This,
- /* [out] */ const void __RPC_FAR *__RPC_FAR *ppIdentity,
- /* [out] */ IID __RPC_FAR *piid,
- /* [out] */ DWORD __RPC_FAR *pdwMethod,
- /* [out] */ HRESULT __RPC_FAR *phr);
-
-
-void __RPC_STUB ICall_GetCallInfo_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE ICall_Nullify_Proxy(
- ICall __RPC_FAR * This,
- /* [in] */ HRESULT hr);
-
-
-void __RPC_STUB ICall_Nullify_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE ICall_GetServerHR_Proxy(
- ICall __RPC_FAR * This,
- /* [out] */ HRESULT __RPC_FAR *phr);
-
-
-void __RPC_STUB ICall_GetServerHR_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __ICall_INTERFACE_DEFINED__ */
-
-
-#ifndef __IRpcCall_INTERFACE_DEFINED__
-#define __IRpcCall_INTERFACE_DEFINED__
-
-/* interface IRpcCall */
-/* [unique][uuid][object][local] */
-
-typedef /* [unique] */ IRpcCall __RPC_FAR *LPRPCCALL;
-
-
-EXTERN_C const IID IID_IRpcCall;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c5-0000-0000-C000-000000000046")
- IRpcCall : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetRpcOleMessage(
- /* [out] */ RPCOLEMESSAGE __RPC_FAR *__RPC_FAR *ppMessage) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IRpcCallVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IRpcCall __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IRpcCall __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IRpcCall __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRpcOleMessage )(
- IRpcCall __RPC_FAR * This,
- /* [out] */ RPCOLEMESSAGE __RPC_FAR *__RPC_FAR *ppMessage);
-
- END_INTERFACE
- } IRpcCallVtbl;
-
- interface IRpcCall
- {
- CONST_VTBL struct IRpcCallVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IRpcCall_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IRpcCall_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IRpcCall_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IRpcCall_GetRpcOleMessage(This,ppMessage) \
- (This)->lpVtbl -> GetRpcOleMessage(This,ppMessage)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IRpcCall_GetRpcOleMessage_Proxy(
- IRpcCall __RPC_FAR * This,
- /* [out] */ RPCOLEMESSAGE __RPC_FAR *__RPC_FAR *ppMessage);
-
-
-void __RPC_STUB IRpcCall_GetRpcOleMessage_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IRpcCall_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_contxt_0083 */
-/* [local] */
-
-typedef
-enum _CALLSOURCE
- { CALLSOURCE_CROSSAPT = 0,
- CALLSOURCE_CROSSCTX = 1
- } CALLSOURCE;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0083_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0083_v0_0_s_ifspec;
-
-#ifndef __ICallInfo_INTERFACE_DEFINED__
-#define __ICallInfo_INTERFACE_DEFINED__
-
-/* interface ICallInfo */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_ICallInfo;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001dc-0000-0000-C000-000000000046")
- ICallInfo : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetCallSource(
- /* [out] */ CALLSOURCE __RPC_FAR *pCallSource) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct ICallInfoVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- ICallInfo __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- ICallInfo __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- ICallInfo __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCallSource )(
- ICallInfo __RPC_FAR * This,
- /* [out] */ CALLSOURCE __RPC_FAR *pCallSource);
-
- END_INTERFACE
- } ICallInfoVtbl;
-
- interface ICallInfo
- {
- CONST_VTBL struct ICallInfoVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICallInfo_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define ICallInfo_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define ICallInfo_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define ICallInfo_GetCallSource(This,pCallSource) \
- (This)->lpVtbl -> GetCallSource(This,pCallSource)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE ICallInfo_GetCallSource_Proxy(
- ICallInfo __RPC_FAR * This,
- /* [out] */ CALLSOURCE __RPC_FAR *pCallSource);
-
-
-void __RPC_STUB ICallInfo_GetCallSource_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __ICallInfo_INTERFACE_DEFINED__ */
-
-
-#ifndef __IPolicy_INTERFACE_DEFINED__
-#define __IPolicy_INTERFACE_DEFINED__
-
-/* interface IPolicy */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IPolicy;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c2-0000-0000-C000-000000000046")
- IPolicy : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Call(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Enter(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Leave(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Return(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CallGetSize(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CallFillBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE EnterWithBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE LeaveGetSize(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE LeaveFillBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ReturnWithBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb) = 0;
-
- virtual ULONG STDMETHODCALLTYPE AddRefPolicy( void) = 0;
-
- virtual ULONG STDMETHODCALLTYPE ReleasePolicy( void) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IPolicyVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IPolicy __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IPolicy __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IPolicy __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Call )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Enter )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Leave )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Return )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CallGetSize )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CallFillBuffer )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnterWithBuffer )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LeaveGetSize )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LeaveFillBuffer )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReturnWithBuffer )(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRefPolicy )(
- IPolicy __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *ReleasePolicy )(
- IPolicy __RPC_FAR * This);
-
- END_INTERFACE
- } IPolicyVtbl;
-
- interface IPolicy
- {
- CONST_VTBL struct IPolicyVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IPolicy_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IPolicy_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IPolicy_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IPolicy_Call(This,pCall) \
- (This)->lpVtbl -> Call(This,pCall)
-
-#define IPolicy_Enter(This,pCall) \
- (This)->lpVtbl -> Enter(This,pCall)
-
-#define IPolicy_Leave(This,pCall) \
- (This)->lpVtbl -> Leave(This,pCall)
-
-#define IPolicy_Return(This,pCall) \
- (This)->lpVtbl -> Return(This,pCall)
-
-#define IPolicy_CallGetSize(This,pCall,pcb) \
- (This)->lpVtbl -> CallGetSize(This,pCall,pcb)
-
-#define IPolicy_CallFillBuffer(This,pCall,pvBuf,pcb) \
- (This)->lpVtbl -> CallFillBuffer(This,pCall,pvBuf,pcb)
-
-#define IPolicy_EnterWithBuffer(This,pCall,pvBuf,cb) \
- (This)->lpVtbl -> EnterWithBuffer(This,pCall,pvBuf,cb)
-
-#define IPolicy_LeaveGetSize(This,pCall,pcb) \
- (This)->lpVtbl -> LeaveGetSize(This,pCall,pcb)
-
-#define IPolicy_LeaveFillBuffer(This,pCall,pvBuf,pcb) \
- (This)->lpVtbl -> LeaveFillBuffer(This,pCall,pvBuf,pcb)
-
-#define IPolicy_ReturnWithBuffer(This,pCall,pvBuf,cb) \
- (This)->lpVtbl -> ReturnWithBuffer(This,pCall,pvBuf,cb)
-
-#define IPolicy_AddRefPolicy(This) \
- (This)->lpVtbl -> AddRefPolicy(This)
-
-#define IPolicy_ReleasePolicy(This) \
- (This)->lpVtbl -> ReleasePolicy(This)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_Call_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicy_Call_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_Enter_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicy_Enter_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_Leave_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicy_Leave_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_Return_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicy_Return_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_CallGetSize_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicy_CallGetSize_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_CallFillBuffer_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicy_CallFillBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_EnterWithBuffer_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
-
-void __RPC_STUB IPolicy_EnterWithBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_LeaveGetSize_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicy_LeaveGetSize_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_LeaveFillBuffer_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicy_LeaveFillBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicy_ReturnWithBuffer_Proxy(
- IPolicy __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
-
-void __RPC_STUB IPolicy_ReturnWithBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-ULONG STDMETHODCALLTYPE IPolicy_AddRefPolicy_Proxy(
- IPolicy __RPC_FAR * This);
-
-
-void __RPC_STUB IPolicy_AddRefPolicy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-ULONG STDMETHODCALLTYPE IPolicy_ReleasePolicy_Proxy(
- IPolicy __RPC_FAR * This);
-
-
-void __RPC_STUB IPolicy_ReleasePolicy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IPolicy_INTERFACE_DEFINED__ */
-
-
-#ifndef __IPolicyAsync_INTERFACE_DEFINED__
-#define __IPolicyAsync_INTERFACE_DEFINED__
-
-/* interface IPolicyAsync */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IPolicyAsync;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001cd-0000-0000-C000-000000000046")
- IPolicyAsync : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE BeginCallGetSize(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginCall(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginCallFillBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginEnter(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginEnterWithBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginLeave(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginReturn(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishCall(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishEnter(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishLeaveGetSize(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishLeave(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishLeaveFillBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishReturn(
- /* [in] */ ICall __RPC_FAR *pCall) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FinishReturnWithBuffer(
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IPolicyAsyncVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IPolicyAsync __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IPolicyAsync __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginCallGetSize )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginCall )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginCallFillBuffer )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginEnter )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginEnterWithBuffer )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginLeave )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *BeginReturn )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishCall )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishEnter )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishLeaveGetSize )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishLeave )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishLeaveFillBuffer )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishReturn )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FinishReturnWithBuffer )(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
- END_INTERFACE
- } IPolicyAsyncVtbl;
-
- interface IPolicyAsync
- {
- CONST_VTBL struct IPolicyAsyncVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IPolicyAsync_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IPolicyAsync_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IPolicyAsync_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IPolicyAsync_BeginCallGetSize(This,pCall,pcb) \
- (This)->lpVtbl -> BeginCallGetSize(This,pCall,pcb)
-
-#define IPolicyAsync_BeginCall(This,pCall) \
- (This)->lpVtbl -> BeginCall(This,pCall)
-
-#define IPolicyAsync_BeginCallFillBuffer(This,pCall,pvBuf,pcb) \
- (This)->lpVtbl -> BeginCallFillBuffer(This,pCall,pvBuf,pcb)
-
-#define IPolicyAsync_BeginEnter(This,pCall) \
- (This)->lpVtbl -> BeginEnter(This,pCall)
-
-#define IPolicyAsync_BeginEnterWithBuffer(This,pCall,pvBuf,cb) \
- (This)->lpVtbl -> BeginEnterWithBuffer(This,pCall,pvBuf,cb)
-
-#define IPolicyAsync_BeginLeave(This,pCall) \
- (This)->lpVtbl -> BeginLeave(This,pCall)
-
-#define IPolicyAsync_BeginReturn(This,pCall) \
- (This)->lpVtbl -> BeginReturn(This,pCall)
-
-#define IPolicyAsync_FinishCall(This,pCall) \
- (This)->lpVtbl -> FinishCall(This,pCall)
-
-#define IPolicyAsync_FinishEnter(This,pCall) \
- (This)->lpVtbl -> FinishEnter(This,pCall)
-
-#define IPolicyAsync_FinishLeaveGetSize(This,pCall,pcb) \
- (This)->lpVtbl -> FinishLeaveGetSize(This,pCall,pcb)
-
-#define IPolicyAsync_FinishLeave(This,pCall) \
- (This)->lpVtbl -> FinishLeave(This,pCall)
-
-#define IPolicyAsync_FinishLeaveFillBuffer(This,pCall,pvBuf,pcb) \
- (This)->lpVtbl -> FinishLeaveFillBuffer(This,pCall,pvBuf,pcb)
-
-#define IPolicyAsync_FinishReturn(This,pCall) \
- (This)->lpVtbl -> FinishReturn(This,pCall)
-
-#define IPolicyAsync_FinishReturnWithBuffer(This,pCall,pvBuf,cb) \
- (This)->lpVtbl -> FinishReturnWithBuffer(This,pCall,pvBuf,cb)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginCallGetSize_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicyAsync_BeginCallGetSize_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginCall_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_BeginCall_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginCallFillBuffer_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicyAsync_BeginCallFillBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginEnter_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_BeginEnter_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginEnterWithBuffer_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
-
-void __RPC_STUB IPolicyAsync_BeginEnterWithBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginLeave_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_BeginLeave_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_BeginReturn_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_BeginReturn_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishCall_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_FinishCall_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishEnter_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_FinishEnter_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishLeaveGetSize_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicyAsync_FinishLeaveGetSize_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishLeave_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_FinishLeave_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishLeaveFillBuffer_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [out] */ ULONG __RPC_FAR *pcb);
-
-
-void __RPC_STUB IPolicyAsync_FinishLeaveFillBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishReturn_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall);
-
-
-void __RPC_STUB IPolicyAsync_FinishReturn_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyAsync_FinishReturnWithBuffer_Proxy(
- IPolicyAsync __RPC_FAR * This,
- /* [in] */ ICall __RPC_FAR *pCall,
- /* [in] */ void __RPC_FAR *pvBuf,
- /* [in] */ ULONG cb);
-
-
-void __RPC_STUB IPolicyAsync_FinishReturnWithBuffer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IPolicyAsync_INTERFACE_DEFINED__ */
-
-
-#ifndef __IPolicySet_INTERFACE_DEFINED__
-#define __IPolicySet_INTERFACE_DEFINED__
-
-/* interface IPolicySet */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IPolicySet;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c3-0000-0000-C000-000000000046")
- IPolicySet : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE AddPolicy(
- /* [in] */ ContextEvent ctxEvent,
- /* [in] */ REFGUID rguid,
- /* [in] */ IPolicy __RPC_FAR *pPolicy) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IPolicySetVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IPolicySet __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IPolicySet __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IPolicySet __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddPolicy )(
- IPolicySet __RPC_FAR * This,
- /* [in] */ ContextEvent ctxEvent,
- /* [in] */ REFGUID rguid,
- /* [in] */ IPolicy __RPC_FAR *pPolicy);
-
- END_INTERFACE
- } IPolicySetVtbl;
-
- interface IPolicySet
- {
- CONST_VTBL struct IPolicySetVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IPolicySet_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IPolicySet_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IPolicySet_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IPolicySet_AddPolicy(This,ctxEvent,rguid,pPolicy) \
- (This)->lpVtbl -> AddPolicy(This,ctxEvent,rguid,pPolicy)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IPolicySet_AddPolicy_Proxy(
- IPolicySet __RPC_FAR * This,
- /* [in] */ ContextEvent ctxEvent,
- /* [in] */ REFGUID rguid,
- /* [in] */ IPolicy __RPC_FAR *pPolicy);
-
-
-void __RPC_STUB IPolicySet_AddPolicy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IPolicySet_INTERFACE_DEFINED__ */
-
-
-#ifndef __IComObjIdentity_INTERFACE_DEFINED__
-#define __IComObjIdentity_INTERFACE_DEFINED__
-
-/* interface IComObjIdentity */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IComObjIdentity;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001d7-0000-0000-C000-000000000046")
- IComObjIdentity : public IUnknown
- {
- public:
- virtual BOOL STDMETHODCALLTYPE IsServer( void) = 0;
-
- virtual BOOL STDMETHODCALLTYPE IsDeactivated( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetIdentity(
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IComObjIdentityVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IComObjIdentity __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IComObjIdentity __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IComObjIdentity __RPC_FAR * This);
-
- BOOL ( STDMETHODCALLTYPE __RPC_FAR *IsServer )(
- IComObjIdentity __RPC_FAR * This);
-
- BOOL ( STDMETHODCALLTYPE __RPC_FAR *IsDeactivated )(
- IComObjIdentity __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIdentity )(
- IComObjIdentity __RPC_FAR * This,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk);
-
- END_INTERFACE
- } IComObjIdentityVtbl;
-
- interface IComObjIdentity
- {
- CONST_VTBL struct IComObjIdentityVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IComObjIdentity_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IComObjIdentity_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IComObjIdentity_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IComObjIdentity_IsServer(This) \
- (This)->lpVtbl -> IsServer(This)
-
-#define IComObjIdentity_IsDeactivated(This) \
- (This)->lpVtbl -> IsDeactivated(This)
-
-#define IComObjIdentity_GetIdentity(This,ppUnk) \
- (This)->lpVtbl -> GetIdentity(This,ppUnk)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-BOOL STDMETHODCALLTYPE IComObjIdentity_IsServer_Proxy(
- IComObjIdentity __RPC_FAR * This);
-
-
-void __RPC_STUB IComObjIdentity_IsServer_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-BOOL STDMETHODCALLTYPE IComObjIdentity_IsDeactivated_Proxy(
- IComObjIdentity __RPC_FAR * This);
-
-
-void __RPC_STUB IComObjIdentity_IsDeactivated_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IComObjIdentity_GetIdentity_Proxy(
- IComObjIdentity __RPC_FAR * This,
- /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk);
-
-
-void __RPC_STUB IComObjIdentity_GetIdentity_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IComObjIdentity_INTERFACE_DEFINED__ */
-
-
-#ifndef __IPolicyMaker_INTERFACE_DEFINED__
-#define __IPolicyMaker_INTERFACE_DEFINED__
-
-/* interface IPolicyMaker */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IPolicyMaker;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c4-0000-0000-C000-000000000046")
- IPolicyMaker : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE AddClientPoliciesToSet(
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE AddEnvoyPoliciesToSet(
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE AddServerPoliciesToSet(
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Freeze(
- /* [in] */ IObjContext __RPC_FAR *pObjContext) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CreateStub(
- /* [in] */ IComObjIdentity __RPC_FAR *pID) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DestroyStub(
- /* [in] */ IComObjIdentity __RPC_FAR *pID) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CreateProxy(
- /* [in] */ IComObjIdentity __RPC_FAR *pID) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DestroyProxy(
- /* [in] */ IComObjIdentity __RPC_FAR *pID) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IPolicyMakerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IPolicyMaker __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IPolicyMaker __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddClientPoliciesToSet )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddEnvoyPoliciesToSet )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddServerPoliciesToSet )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Freeze )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IObjContext __RPC_FAR *pObjContext);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateStub )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DestroyStub )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateProxy )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DestroyProxy )(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
- END_INTERFACE
- } IPolicyMakerVtbl;
-
- interface IPolicyMaker
- {
- CONST_VTBL struct IPolicyMakerVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IPolicyMaker_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IPolicyMaker_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IPolicyMaker_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IPolicyMaker_AddClientPoliciesToSet(This,pPS,pClientContext,pServerContext) \
- (This)->lpVtbl -> AddClientPoliciesToSet(This,pPS,pClientContext,pServerContext)
-
-#define IPolicyMaker_AddEnvoyPoliciesToSet(This,pPS,pClientContext,pServerContext) \
- (This)->lpVtbl -> AddEnvoyPoliciesToSet(This,pPS,pClientContext,pServerContext)
-
-#define IPolicyMaker_AddServerPoliciesToSet(This,pPS,pClientContext,pServerContext) \
- (This)->lpVtbl -> AddServerPoliciesToSet(This,pPS,pClientContext,pServerContext)
-
-#define IPolicyMaker_Freeze(This,pObjContext) \
- (This)->lpVtbl -> Freeze(This,pObjContext)
-
-#define IPolicyMaker_CreateStub(This,pID) \
- (This)->lpVtbl -> CreateStub(This,pID)
-
-#define IPolicyMaker_DestroyStub(This,pID) \
- (This)->lpVtbl -> DestroyStub(This,pID)
-
-#define IPolicyMaker_CreateProxy(This,pID) \
- (This)->lpVtbl -> CreateProxy(This,pID)
-
-#define IPolicyMaker_DestroyProxy(This,pID) \
- (This)->lpVtbl -> DestroyProxy(This,pID)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_AddClientPoliciesToSet_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
-
-void __RPC_STUB IPolicyMaker_AddClientPoliciesToSet_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_AddEnvoyPoliciesToSet_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
-
-void __RPC_STUB IPolicyMaker_AddEnvoyPoliciesToSet_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_AddServerPoliciesToSet_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IPolicySet __RPC_FAR *pPS,
- /* [in] */ IContext __RPC_FAR *pClientContext,
- /* [in] */ IContext __RPC_FAR *pServerContext);
-
-
-void __RPC_STUB IPolicyMaker_AddServerPoliciesToSet_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_Freeze_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IObjContext __RPC_FAR *pObjContext);
-
-
-void __RPC_STUB IPolicyMaker_Freeze_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_CreateStub_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
-
-void __RPC_STUB IPolicyMaker_CreateStub_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_DestroyStub_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
-
-void __RPC_STUB IPolicyMaker_DestroyStub_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_CreateProxy_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
-
-void __RPC_STUB IPolicyMaker_CreateProxy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IPolicyMaker_DestroyProxy_Proxy(
- IPolicyMaker __RPC_FAR * This,
- /* [in] */ IComObjIdentity __RPC_FAR *pID);
-
-
-void __RPC_STUB IPolicyMaker_DestroyProxy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IPolicyMaker_INTERFACE_DEFINED__ */
-
-
-#ifndef __IExceptionNotification_INTERFACE_DEFINED__
-#define __IExceptionNotification_INTERFACE_DEFINED__
-
-/* interface IExceptionNotification */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IExceptionNotification;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001db-0000-0000-C000-000000000046")
- IExceptionNotification : public IUnknown
- {
- public:
- virtual void STDMETHODCALLTYPE ServerException(
- /* [in] */ void __RPC_FAR *pExcepPtrs) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IExceptionNotificationVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IExceptionNotification __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IExceptionNotification __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IExceptionNotification __RPC_FAR * This);
-
- void ( STDMETHODCALLTYPE __RPC_FAR *ServerException )(
- IExceptionNotification __RPC_FAR * This,
- /* [in] */ void __RPC_FAR *pExcepPtrs);
-
- END_INTERFACE
- } IExceptionNotificationVtbl;
-
- interface IExceptionNotification
- {
- CONST_VTBL struct IExceptionNotificationVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IExceptionNotification_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IExceptionNotification_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IExceptionNotification_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IExceptionNotification_ServerException(This,pExcepPtrs) \
- (This)->lpVtbl -> ServerException(This,pExcepPtrs)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-void STDMETHODCALLTYPE IExceptionNotification_ServerException_Proxy(
- IExceptionNotification __RPC_FAR * This,
- /* [in] */ void __RPC_FAR *pExcepPtrs);
-
-
-void __RPC_STUB IExceptionNotification_ServerException_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IExceptionNotification_INTERFACE_DEFINED__ */
-
-
-#ifndef __IMarshalEnvoy_INTERFACE_DEFINED__
-#define __IMarshalEnvoy_INTERFACE_DEFINED__
-
-/* interface IMarshalEnvoy */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IMarshalEnvoy;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001c8-0000-0000-C000-000000000046")
- IMarshalEnvoy : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetEnvoyUnmarshalClass(
- /* [in] */ DWORD dwDestContext,
- /* [out] */ CLSID __RPC_FAR *pClsid) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetEnvoySizeMax(
- /* [in] */ DWORD dwDestContext,
- /* [out] */ DWORD __RPC_FAR *pcb) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE MarshalEnvoy(
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ DWORD dwDestContext) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE UnmarshalEnvoy(
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppunk) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IMarshalEnvoyVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IMarshalEnvoy __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IMarshalEnvoy __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetEnvoyUnmarshalClass )(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ DWORD dwDestContext,
- /* [out] */ CLSID __RPC_FAR *pClsid);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetEnvoySizeMax )(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ DWORD dwDestContext,
- /* [out] */ DWORD __RPC_FAR *pcb);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MarshalEnvoy )(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ DWORD dwDestContext);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UnmarshalEnvoy )(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppunk);
-
- END_INTERFACE
- } IMarshalEnvoyVtbl;
-
- interface IMarshalEnvoy
- {
- CONST_VTBL struct IMarshalEnvoyVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IMarshalEnvoy_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IMarshalEnvoy_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IMarshalEnvoy_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IMarshalEnvoy_GetEnvoyUnmarshalClass(This,dwDestContext,pClsid) \
- (This)->lpVtbl -> GetEnvoyUnmarshalClass(This,dwDestContext,pClsid)
-
-#define IMarshalEnvoy_GetEnvoySizeMax(This,dwDestContext,pcb) \
- (This)->lpVtbl -> GetEnvoySizeMax(This,dwDestContext,pcb)
-
-#define IMarshalEnvoy_MarshalEnvoy(This,pStream,dwDestContext) \
- (This)->lpVtbl -> MarshalEnvoy(This,pStream,dwDestContext)
-
-#define IMarshalEnvoy_UnmarshalEnvoy(This,pStream,riid,ppunk) \
- (This)->lpVtbl -> UnmarshalEnvoy(This,pStream,riid,ppunk)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IMarshalEnvoy_GetEnvoyUnmarshalClass_Proxy(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ DWORD dwDestContext,
- /* [out] */ CLSID __RPC_FAR *pClsid);
-
-
-void __RPC_STUB IMarshalEnvoy_GetEnvoyUnmarshalClass_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IMarshalEnvoy_GetEnvoySizeMax_Proxy(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ DWORD dwDestContext,
- /* [out] */ DWORD __RPC_FAR *pcb);
-
-
-void __RPC_STUB IMarshalEnvoy_GetEnvoySizeMax_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IMarshalEnvoy_MarshalEnvoy_Proxy(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ DWORD dwDestContext);
-
-
-void __RPC_STUB IMarshalEnvoy_MarshalEnvoy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IMarshalEnvoy_UnmarshalEnvoy_Proxy(
- IMarshalEnvoy __RPC_FAR * This,
- /* [in] */ IStream __RPC_FAR *pStream,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppunk);
-
-
-void __RPC_STUB IMarshalEnvoy_UnmarshalEnvoy_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IMarshalEnvoy_INTERFACE_DEFINED__ */
-
-
-#ifndef __IWrapperInfo_INTERFACE_DEFINED__
-#define __IWrapperInfo_INTERFACE_DEFINED__
-
-/* interface IWrapperInfo */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IWrapperInfo;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("5052f924-7ab8-11d3-b93f-00c04f990176")
- IWrapperInfo : public IUnknown
- {
- public:
- virtual void STDMETHODCALLTYPE SetMapping(
- void __RPC_FAR *pv) = 0;
-
- virtual void __RPC_FAR *STDMETHODCALLTYPE GetMapping( void) = 0;
-
- virtual IObjContext __RPC_FAR *STDMETHODCALLTYPE GetServerObjectContext( void) = 0;
-
- virtual IUnknown __RPC_FAR *STDMETHODCALLTYPE GetServerObject( void) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IWrapperInfoVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IWrapperInfo __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IWrapperInfo __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IWrapperInfo __RPC_FAR * This);
-
- void ( STDMETHODCALLTYPE __RPC_FAR *SetMapping )(
- IWrapperInfo __RPC_FAR * This,
- void __RPC_FAR *pv);
-
- void __RPC_FAR *( STDMETHODCALLTYPE __RPC_FAR *GetMapping )(
- IWrapperInfo __RPC_FAR * This);
-
- IObjContext __RPC_FAR *( STDMETHODCALLTYPE __RPC_FAR *GetServerObjectContext )(
- IWrapperInfo __RPC_FAR * This);
-
- IUnknown __RPC_FAR *( STDMETHODCALLTYPE __RPC_FAR *GetServerObject )(
- IWrapperInfo __RPC_FAR * This);
-
- END_INTERFACE
- } IWrapperInfoVtbl;
-
- interface IWrapperInfo
- {
- CONST_VTBL struct IWrapperInfoVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IWrapperInfo_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IWrapperInfo_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IWrapperInfo_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IWrapperInfo_SetMapping(This,pv) \
- (This)->lpVtbl -> SetMapping(This,pv)
-
-#define IWrapperInfo_GetMapping(This) \
- (This)->lpVtbl -> GetMapping(This)
-
-#define IWrapperInfo_GetServerObjectContext(This) \
- (This)->lpVtbl -> GetServerObjectContext(This)
-
-#define IWrapperInfo_GetServerObject(This) \
- (This)->lpVtbl -> GetServerObject(This)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-void STDMETHODCALLTYPE IWrapperInfo_SetMapping_Proxy(
- IWrapperInfo __RPC_FAR * This,
- void __RPC_FAR *pv);
-
-
-void __RPC_STUB IWrapperInfo_SetMapping_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-void __RPC_FAR *STDMETHODCALLTYPE IWrapperInfo_GetMapping_Proxy(
- IWrapperInfo __RPC_FAR * This);
-
-
-void __RPC_STUB IWrapperInfo_GetMapping_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-IObjContext __RPC_FAR *STDMETHODCALLTYPE IWrapperInfo_GetServerObjectContext_Proxy(
- IWrapperInfo __RPC_FAR * This);
-
-
-void __RPC_STUB IWrapperInfo_GetServerObjectContext_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-IUnknown __RPC_FAR *STDMETHODCALLTYPE IWrapperInfo_GetServerObject_Proxy(
- IWrapperInfo __RPC_FAR * This);
-
-
-void __RPC_STUB IWrapperInfo_GetServerObject_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IWrapperInfo_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_contxt_0092 */
-/* [local] */
-
-
-typedef DWORD APARTMENTID;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0092_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0092_v0_0_s_ifspec;
-
-#ifndef __IComThreadingInfo_INTERFACE_DEFINED__
-#define __IComThreadingInfo_INTERFACE_DEFINED__
-
-/* interface IComThreadingInfo */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IComThreadingInfo;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001ce-0000-0000-C000-000000000046")
- IComThreadingInfo : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetCurrentApartmentType(
- /* [out] */ APTTYPE __RPC_FAR *pAptType) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadType(
- /* [out] */ THDTYPE __RPC_FAR *pThreadType) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetCurrentLogicalThreadId(
- /* [out] */ GUID __RPC_FAR *pguidLogicalThreadId) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetCurrentLogicalThreadId(
- /* [in] */ REFGUID rguid) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IComThreadingInfoVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IComThreadingInfo __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IComThreadingInfo __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IComThreadingInfo __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCurrentApartmentType )(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ APTTYPE __RPC_FAR *pAptType);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCurrentThreadType )(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ THDTYPE __RPC_FAR *pThreadType);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCurrentLogicalThreadId )(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ GUID __RPC_FAR *pguidLogicalThreadId);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCurrentLogicalThreadId )(
- IComThreadingInfo __RPC_FAR * This,
- /* [in] */ REFGUID rguid);
-
- END_INTERFACE
- } IComThreadingInfoVtbl;
-
- interface IComThreadingInfo
- {
- CONST_VTBL struct IComThreadingInfoVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IComThreadingInfo_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IComThreadingInfo_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IComThreadingInfo_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IComThreadingInfo_GetCurrentApartmentType(This,pAptType) \
- (This)->lpVtbl -> GetCurrentApartmentType(This,pAptType)
-
-#define IComThreadingInfo_GetCurrentThreadType(This,pThreadType) \
- (This)->lpVtbl -> GetCurrentThreadType(This,pThreadType)
-
-#define IComThreadingInfo_GetCurrentLogicalThreadId(This,pguidLogicalThreadId) \
- (This)->lpVtbl -> GetCurrentLogicalThreadId(This,pguidLogicalThreadId)
-
-#define IComThreadingInfo_SetCurrentLogicalThreadId(This,rguid) \
- (This)->lpVtbl -> SetCurrentLogicalThreadId(This,rguid)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IComThreadingInfo_GetCurrentApartmentType_Proxy(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ APTTYPE __RPC_FAR *pAptType);
-
-
-void __RPC_STUB IComThreadingInfo_GetCurrentApartmentType_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IComThreadingInfo_GetCurrentThreadType_Proxy(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ THDTYPE __RPC_FAR *pThreadType);
-
-
-void __RPC_STUB IComThreadingInfo_GetCurrentThreadType_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IComThreadingInfo_GetCurrentLogicalThreadId_Proxy(
- IComThreadingInfo __RPC_FAR * This,
- /* [out] */ GUID __RPC_FAR *pguidLogicalThreadId);
-
-
-void __RPC_STUB IComThreadingInfo_GetCurrentLogicalThreadId_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IComThreadingInfo_SetCurrentLogicalThreadId_Proxy(
- IComThreadingInfo __RPC_FAR * This,
- /* [in] */ REFGUID rguid);
-
-
-void __RPC_STUB IComThreadingInfo_SetCurrentLogicalThreadId_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IComThreadingInfo_INTERFACE_DEFINED__ */
-
-
-#ifndef __IComDispatchInfo_INTERFACE_DEFINED__
-#define __IComDispatchInfo_INTERFACE_DEFINED__
-
-/* interface IComDispatchInfo */
-/* [unique][uuid][object][local] */
-
-
-EXTERN_C const IID IID_IComDispatchInfo;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("000001d9-0000-0000-C000-000000000046")
- IComDispatchInfo : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE EnableComInits(
- /* [out] */ void __RPC_FAR *__RPC_FAR *ppvCookie) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DisableComInits(
- /* [in] */ void __RPC_FAR *pvCookie) = 0;
-
- };
-
-#else /* C style interface */
-
- typedef struct IComDispatchInfoVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
- IComDispatchInfo __RPC_FAR * This,
- /* [in] */ REFIID riid,
- /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
- IComDispatchInfo __RPC_FAR * This);
-
- ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
- IComDispatchInfo __RPC_FAR * This);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnableComInits )(
- IComDispatchInfo __RPC_FAR * This,
- /* [out] */ void __RPC_FAR *__RPC_FAR *ppvCookie);
-
- HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DisableComInits )(
- IComDispatchInfo __RPC_FAR * This,
- /* [in] */ void __RPC_FAR *pvCookie);
-
- END_INTERFACE
- } IComDispatchInfoVtbl;
-
- interface IComDispatchInfo
- {
- CONST_VTBL struct IComDispatchInfoVtbl __RPC_FAR *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define IComDispatchInfo_QueryInterface(This,riid,ppvObject) \
- (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
-
-#define IComDispatchInfo_AddRef(This) \
- (This)->lpVtbl -> AddRef(This)
-
-#define IComDispatchInfo_Release(This) \
- (This)->lpVtbl -> Release(This)
-
-
-#define IComDispatchInfo_EnableComInits(This,ppvCookie) \
- (This)->lpVtbl -> EnableComInits(This,ppvCookie)
-
-#define IComDispatchInfo_DisableComInits(This,pvCookie) \
- (This)->lpVtbl -> DisableComInits(This,pvCookie)
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-HRESULT STDMETHODCALLTYPE IComDispatchInfo_EnableComInits_Proxy(
- IComDispatchInfo __RPC_FAR * This,
- /* [out] */ void __RPC_FAR *__RPC_FAR *ppvCookie);
-
-
-void __RPC_STUB IComDispatchInfo_EnableComInits_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-HRESULT STDMETHODCALLTYPE IComDispatchInfo_DisableComInits_Proxy(
- IComDispatchInfo __RPC_FAR * This,
- /* [in] */ void __RPC_FAR *pvCookie);
-
-
-void __RPC_STUB IComDispatchInfo_DisableComInits_Stub(
- IRpcStubBuffer *This,
- IRpcChannelBuffer *_pRpcChannelBuffer,
- PRPC_MESSAGE _pRpcMessage,
- DWORD *_pdwStubPhase);
-
-
-
-#endif /* __IComDispatchInfo_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_contxt_0094 */
-/* [local] */
-
-typedef DWORD HActivator;
-
-STDAPI CoCreateObjectInContext(IUnknown *pUnk, IObjContext *pObjectCtx, REFIID riid, void **ppv);
-STDAPI CoGetApartmentID(APTTYPE dAptType, HActivator* pAptID);
-STDAPI CoDeactivateObject(IUnknown *pUnk, IUnknown **ppCookie);
-STDAPI CoReactivateObject(IUnknown *pUnk, IUnknown *pCookie);
-#define MSHLFLAGS_NO_IEC 0x8 // don't use IExternalConnextion
-#define MSHLFLAGS_NO_IMARSHAL 0x10 // don't use IMarshal
-#define CONTEXTFLAGS_FROZEN 0x01 // Frozen context
-#define CONTEXTFLAGS_ALLOWUNAUTH 0x02 // Allow unauthenticated calls
-#define CONTEXTFLAGS_ENVOYCONTEXT 0x04 // Envoy context
-#define CONTEXTFLAGS_DEFAULTCONTEXT 0x08 // Default context
-#define CONTEXTFLAGS_STATICCONTEXT 0x10 // Static context
-#define CONTEXTFLAGS_INPROPTABLE 0x20 // Is in property table
-#define CONTEXTFLAGS_INDESTRUCTOR 0x40 // Is in destructor
-#define CONTEXTFLAGS_URTPROPPRESENT 0x80 // CLR property added
-
-
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0094_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_contxt_0094_v0_0_s_ifspec;
-
-/* Additional Prototypes for ALL interfaces */
-
-/* end of Additional Prototypes */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/coreclr/inc/corhlprpriv.h b/src/coreclr/inc/corhlprpriv.h
index 38faa6f6ef8be7..2c8372860ba2e2 100644
--- a/src/coreclr/inc/corhlprpriv.h
+++ b/src/coreclr/inc/corhlprpriv.h
@@ -605,7 +605,7 @@ class RidBitmap
HRESULT hr = S_OK;
mdToken rid = RidFromToken(token);
SIZE_T index = rid / 8;
- BYTE bit = (1 << (rid % 8));
+ BYTE bit = (BYTE)(1 << (rid % 8));
if (index >= buffer.Size())
{
@@ -623,7 +623,7 @@ class RidBitmap
{
mdToken rid = RidFromToken(token);
SIZE_T index = rid / 8;
- BYTE bit = (1 << (rid % 8));
+ BYTE bit = (BYTE)(1 << (rid % 8));
return ((index < buffer.Size()) && (buffer[index] & bit));
}
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 7a673e52d7cc7f..91c5734c905881 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -640,9 +640,14 @@ enum CorInfoHelpFunc
CORINFO_HELP_STACK_PROBE, // Probes each page of the allocated stack frame
CORINFO_HELP_PATCHPOINT, // Notify runtime that code has reached a patchpoint
+ CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT, // Notify runtime that code has reached a part of the method that wasn't originally jitted.
+
CORINFO_HELP_CLASSPROFILE32, // Update 32-bit class profile for a call site
CORINFO_HELP_CLASSPROFILE64, // Update 64-bit class profile for a call site
- CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT, // Notify runtime that code has reached a part of the method that wasn't originally jitted.
+ CORINFO_HELP_DELEGATEPROFILE32, // Update 32-bit method profile for a delegate call site
+ CORINFO_HELP_DELEGATEPROFILE64, // Update 64-bit method profile for a delegate call site
+ CORINFO_HELP_VTABLEPROFILE32, // Update 32-bit method profile for a vtable call site
+ CORINFO_HELP_VTABLEPROFILE64, // Update 64-bit method profile for a vtable call site
CORINFO_HELP_VALIDATE_INDIRECT_CALL, // CFG: Validate function pointer
CORINFO_HELP_DISPATCH_INDIRECT_CALL, // CFG: Validate and dispatch to pointer
@@ -954,11 +959,14 @@ enum CorInfoClassId
enum CorInfoInline
{
- INLINE_PASS = 0, // Inlining OK
+ INLINE_PASS = 0, // Inlining OK
+ INLINE_PREJIT_SUCCESS = 1, // Inline check for prejit checking usage succeeded
+ INLINE_CHECK_CAN_INLINE_SUCCESS = 2, // JIT detected it is permitted to try to actually inline
+ INLINE_CHECK_CAN_INLINE_VMFAIL = 3, // VM specified that inline must fail via the CanInline api
// failures are negative
- INLINE_FAIL = -1, // Inlining not OK for this case only
- INLINE_NEVER = -2, // This method should never be inlined, regardless of context
+ INLINE_FAIL = -1, // Inlining not OK for this case only
+ INLINE_NEVER = -2, // This method should never be inlined, regardless of context
};
enum CorInfoInlineTypeCheck
@@ -2034,6 +2042,11 @@ class ICorStaticInfo
CORINFO_METHOD_HANDLE calleeHnd /* IN */
) = 0;
+ // Report that an inlining related process has begun. This will always be paired with
+ // a call to reportInliningDecision unless the jit fails.
+ virtual void beginInlining (CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd) = 0;
+
// Reports whether or not a method can be inlined, and why. canInline is responsible for reporting all
// inlining results when it returns INLINE_FAIL and INLINE_NEVER. All other results are reported by the
// JIT.
diff --git a/src/coreclr/inc/corjit.h b/src/coreclr/inc/corjit.h
index 54aaded8f90187..380db270e1cbf5 100644
--- a/src/coreclr/inc/corjit.h
+++ b/src/coreclr/inc/corjit.h
@@ -330,7 +330,8 @@ class ICorJitInfo : public ICorDynamicInfo
// Data structure for a single class probe using 32-bit count.
//
- // CLASS_FLAG and INTERFACE_FLAG are placed into the Other field in the schema
+ // CLASS_FLAG, INTERFACE_FLAG and DELEGATE_FLAG are placed into the Other field in the schema.
+ // If CLASS_FLAG is set the handle table consists of type handles, and otherwise method handles.
//
// Count is the number of times a call was made at that call site.
//
@@ -338,8 +339,8 @@ class ICorJitInfo : public ICorDynamicInfo
//
// SAMPLE_INTERVAL must be >= SIZE. SAMPLE_INTERVAL / SIZE
// gives the average number of calls between table updates.
- //
- struct ClassProfile32
+ //
+ struct HandleHistogram32
{
enum
{
@@ -347,17 +348,18 @@ class ICorJitInfo : public ICorDynamicInfo
SAMPLE_INTERVAL = 32,
CLASS_FLAG = 0x80000000,
INTERFACE_FLAG = 0x40000000,
- OFFSET_MASK = 0x3FFFFFFF
+ DELEGATE_FLAG = 0x20000000,
+ OFFSET_MASK = 0x0FFFFFFF
};
uint32_t Count;
- CORINFO_CLASS_HANDLE ClassTable[SIZE];
+ void* HandleTable[SIZE];
};
- struct ClassProfile64
+ struct HandleHistogram64
{
uint64_t Count;
- CORINFO_CLASS_HANDLE ClassTable[ClassProfile32::SIZE];
+ void* HandleTable[HandleHistogram32::SIZE];
};
enum class PgoInstrumentationKind
@@ -387,7 +389,7 @@ class ICorJitInfo : public ICorDynamicInfo
Done = None, // All instrumentation schemas must end with a record which is "Done"
BasicBlockIntCount = (DescriptorMin * 1) | FourByte, // basic block counter using unsigned 4 byte int
BasicBlockLongCount = (DescriptorMin * 1) | EightByte, // basic block counter using unsigned 8 byte int
- HandleHistogramIntCount = (DescriptorMin * 2) | FourByte | AlignPointer, // 4 byte counter that is part of a type histogram. Aligned to match ClassProfile32's alignment.
+ HandleHistogramIntCount = (DescriptorMin * 2) | FourByte | AlignPointer, // 4 byte counter that is part of a type histogram. Aligned to match HandleHistogram32's alignment.
HandleHistogramLongCount = (DescriptorMin * 2) | EightByte, // 8 byte counter that is part of a type histogram
HandleHistogramTypes = (DescriptorMin * 3) | TypeHandle, // Histogram of type handles
HandleHistogramMethods = (DescriptorMin * 3) | MethodHandle, // Histogram of method handles
@@ -396,6 +398,7 @@ class ICorJitInfo : public ICorDynamicInfo
EdgeIntCount = (DescriptorMin * 6) | FourByte, // edge counter using unsigned 4 byte int
EdgeLongCount = (DescriptorMin * 6) | EightByte, // edge counter using unsigned 8 byte int
GetLikelyClass = (DescriptorMin * 7) | TypeHandle, // Compressed get likely class data
+ GetLikelyMethod = (DescriptorMin * 7) | MethodHandle, // Compressed get likely method data
};
struct PgoInstrumentationSchema
@@ -418,7 +421,7 @@ class ICorJitInfo : public ICorDynamicInfo
Sampling= 6, // PGO data derived from sampling
};
-#define DEFAULT_UNKNOWN_TYPEHANDLE 1
+#define DEFAULT_UNKNOWN_HANDLE 1
#define UNKNOWN_HANDLE_MIN 1
#define UNKNOWN_HANDLE_MAX 33
diff --git a/src/coreclr/inc/crosscomp.h b/src/coreclr/inc/crosscomp.h
index d7d8378cd1f845..9a78c69a3423e0 100644
--- a/src/coreclr/inc/crosscomp.h
+++ b/src/coreclr/inc/crosscomp.h
@@ -565,6 +565,8 @@ typedef struct _T_KNONVOLATILE_CONTEXT_POINTERS {
#define DAC_CS_NATIVE_DATA_SIZE 96
#elif defined(TARGET_LINUX) && defined(TARGET_LOONGARCH64)
#define DAC_CS_NATIVE_DATA_SIZE 96
+#elif defined(TARGET_LINUX) && defined(TARGET_POWERPC64)
+#define DAC_CS_NATIVE_DATA_SIZE 96
#elif defined(TARGET_NETBSD) && defined(TARGET_AMD64)
#define DAC_CS_NATIVE_DATA_SIZE 96
#elif defined(TARGET_NETBSD) && defined(TARGET_ARM)
diff --git a/src/coreclr/inc/defaultallocator.h b/src/coreclr/inc/defaultallocator.h
deleted file mode 100644
index 111fb5e7f94c0a..00000000000000
--- a/src/coreclr/inc/defaultallocator.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#ifndef _DEFAULTALLOCATOR_H_
-#define _DEFAULTALLOCATOR_H_
-
-// The "DefaultAllocator" class may be used by classes that wish to
-// provide the flexibility of using an "IAllocator" may avoid writing
-// conditionals at allocation sites about whether a non-default
-// "IAllocator" has been provided: if none is, they can simply set the
-// allocator to DefaultAllocator::Singleton().
-class DefaultAllocator: public IAllocator
-{
- static DefaultAllocator s_singleton;
-
-public:
- void* Alloc(size_t sz)
- {
- return ::operator new(sz);
- }
-
- void* ArrayAlloc(size_t elemSize, size_t numElems)
- {
- ClrSafeInt safeElemSize(elemSize);
- ClrSafeInt safeNumElems(numElems);
- ClrSafeInt sz = safeElemSize * safeNumElems;
- if (sz.IsOverflow())
- {
- return NULL;
- }
- else
- {
- return ::operator new(sz.Value());
- }
- }
-
- virtual void Free(void * p)
- {
- ::operator delete(p);
- }
-
- static DefaultAllocator* Singleton()
- {
- return &s_singleton;
- }
-};
-
-#endif // _DEFAULTALLOCATOR_H_
diff --git a/src/coreclr/inc/iallocator.h b/src/coreclr/inc/iallocator.h
index a5b467a9905b83..f8e0978c6f10ff 100644
--- a/src/coreclr/inc/iallocator.h
+++ b/src/coreclr/inc/iallocator.h
@@ -29,48 +29,4 @@ class IAllocator
virtual void Free(void* p) = 0;
};
-// This class wraps an allocator that does not allow zero-length allocations,
-// producing one that does (every zero-length allocation produces a pointer to the same
-// statically-allocated memory, and freeing that pointer is a no-op).
-class AllowZeroAllocator: public IAllocator
-{
- int m_zeroLenAllocTarg;
- IAllocator* m_alloc;
-
-public:
- AllowZeroAllocator(IAllocator* alloc) : m_alloc(alloc) {}
-
- void* Alloc(size_t sz)
- {
- if (sz == 0)
- {
- return (void*)(&m_zeroLenAllocTarg);
- }
- else
- {
- return m_alloc->Alloc(sz);
- }
- }
-
- void* ArrayAlloc(size_t elemSize, size_t numElems)
- {
- if (elemSize == 0 || numElems == 0)
- {
- return (void*)(&m_zeroLenAllocTarg);
- }
- else
- {
- return m_alloc->ArrayAlloc(elemSize, numElems);
- }
- }
-
- virtual void Free(void * p)
- {
- if (p != (void*)(&m_zeroLenAllocTarg))
- {
- m_alloc->Free(p);
- }
- }
-};
-
#endif // _IALLOCATOR_DEFINED_
diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h
index 87f3054e6269ef..4864b033096944 100644
--- a/src/coreclr/inc/icorjitinfoimpl_generated.h
+++ b/src/coreclr/inc/icorjitinfoimpl_generated.h
@@ -44,6 +44,10 @@ CorInfoInline canInline(
CORINFO_METHOD_HANDLE callerHnd,
CORINFO_METHOD_HANDLE calleeHnd) override;
+void beginInlining(
+ CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd) override;
+
void reportInliningDecision(
CORINFO_METHOD_HANDLE inlinerHnd,
CORINFO_METHOD_HANDLE inlineeHnd,
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index e03d9d9190c605..9a6cbc053e1ce6 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED
-constexpr GUID JITEEVersionIdentifier = { /* 5868685e-b877-4ef5-83f0-73d601e50013 */
- 0x5868685e,
- 0xb877,
- 0x4ef5,
- {0x83, 0xf0, 0x73, 0xd6, 0x01, 0xe5, 0x00, 0x13}
+constexpr GUID JITEEVersionIdentifier = { /* f2faa5fc-a1ec-4244-aebb-5597bfd7153a */
+ 0xf2faa5fc,
+ 0xa1ec,
+ 0x4244,
+ {0xae, 0xbb, 0x55, 0x97, 0xbf, 0xd7, 0x15, 0x3a}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index e40eb4105ee3c0..a500c298978b67 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -328,9 +328,14 @@
#endif
JITHELPER(CORINFO_HELP_PATCHPOINT, JIT_Patchpoint, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT, JIT_PartialCompilationPatchpoint, CORINFO_HELP_SIG_REG_ONLY)
+
JITHELPER(CORINFO_HELP_CLASSPROFILE32, JIT_ClassProfile32, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_CLASSPROFILE64, JIT_ClassProfile64, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT, JIT_PartialCompilationPatchpoint, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_DELEGATEPROFILE32, JIT_DelegateProfile32, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_DELEGATEPROFILE64, JIT_DelegateProfile64, CORINFO_HELP_SIG_REG_ONLY)
+ JITHELPER(CORINFO_HELP_VTABLEPROFILE32, JIT_VTableProfile32, CORINFO_HELP_SIG_4_STACK)
+ JITHELPER(CORINFO_HELP_VTABLEPROFILE64, JIT_VTableProfile64, CORINFO_HELP_SIG_4_STACK)
#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
JITHELPER(CORINFO_HELP_VALIDATE_INDIRECT_CALL, JIT_ValidateIndirectCall, CORINFO_HELP_SIG_REG_ONLY)
diff --git a/src/coreclr/inc/llvm/ELF.h b/src/coreclr/inc/llvm/ELF.h
index 9e89b48b514a3c..b38ecf9eba73a7 100644
--- a/src/coreclr/inc/llvm/ELF.h
+++ b/src/coreclr/inc/llvm/ELF.h
@@ -829,7 +829,7 @@ struct Elf32_Sym {
void setBinding(unsigned char b) { setBindingAndType(b, getType()); }
void setType(unsigned char t) { setBindingAndType(getBinding(), t); }
void setBindingAndType(unsigned char b, unsigned char t) {
- st_info = (b << 4) + (t & 0x0f);
+ st_info = (unsigned char)((b << 4) + (t & 0x0f));
}
};
@@ -849,7 +849,7 @@ struct Elf64_Sym {
void setBinding(unsigned char b) { setBindingAndType(b, getType()); }
void setType(unsigned char t) { setBindingAndType(getBinding(), t); }
void setBindingAndType(unsigned char b, unsigned char t) {
- st_info = (b << 4) + (t & 0x0f);
+ st_info = (unsigned char)((b << 4) + (t & 0x0f));
}
};
diff --git a/src/coreclr/inc/pedecoder.h b/src/coreclr/inc/pedecoder.h
index 7cd145f452082d..2bb79e34b6b01f 100644
--- a/src/coreclr/inc/pedecoder.h
+++ b/src/coreclr/inc/pedecoder.h
@@ -83,6 +83,8 @@ inline CHECK CheckOverflow(RVA value1, COUNT_T value2)
#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_ARM64
#elif defined(TARGET_LOONGARCH64)
#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_LOONGARCH64
+#elif defined(TARGET_POWERPC64)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_POWERPC
#elif defined(TARGET_S390X)
#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_UNKNOWN
#else
diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h
index 76354362d57d83..20db29298cfba1 100644
--- a/src/coreclr/inc/readytorun.h
+++ b/src/coreclr/inc/readytorun.h
@@ -16,7 +16,7 @@
// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
#define READYTORUN_MAJOR_VERSION 0x0006
-#define READYTORUN_MINOR_VERSION 0x0001
+#define READYTORUN_MINOR_VERSION 0x0002
#define MINIMUM_READYTORUN_MAJOR_VERSION 0x006
diff --git a/src/coreclr/inc/regex_base.h b/src/coreclr/inc/regex_base.h
deleted file mode 100644
index fd6103f5da4b31..00000000000000
--- a/src/coreclr/inc/regex_base.h
+++ /dev/null
@@ -1,972 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-//
-// Provides basic interpreted regular expression matching. This is meant as a debugging tool,
-// and if regular expressions become necessary in a non-debug scenario great care should be
-// used to ensure that performance is not impaired, and a more thorough review of this could
-// would also be a good thing. This file does not include any concrete instantiations but
-// instead provides the basic building blocks. Some concrete instantiations can be found in
-// regex_util.h.
-//
-// NOTE: See code:clr::regex::RegExBase (below) for description of supported regex language.
-//
-// NOTE: we had to forego standard options such as tr1::regex
-// (http://en.wikipedia.org/wiki/Technical_Report_1#Regular_Expressions) and Microsoft's
-// internal GRETA regular expressions (http://toolbox/sites/987/default.aspx) because they
-// both rely heavily on the STL, which can not currently be used within the CLR.
-//
-// NOTE: If this becomes non-debug-only, then read the comment on WCHARItemTraits for what
-// what needs fixing.
-//
-
-//
-
-#ifndef _REGEX_BASE_H_
-#define _REGEX_BASE_H_
-
-// Forward declare namespace so that it is not debug-only (even if currently there is nothing
-// but debug-only code in the namespace). This enables a "using namespace clr;" line in a
-// header file without having to worry about whether or not it's in a debug-only block.
-namespace clr {}
-
-#ifdef _DEBUG
-
-#include "utilcode.h" // for string hash functions
-#include "sstring.h"
-
-namespace clr {
-namespace regex {
-
-// Implementation details. Code contained in any "imp" namespace should never be directly used
-// by clients of RegEx.
-namespace imp {
-
- //===================================================================================================
- // Helper for clr::regex::RegExBase. See class definition for clr::regex::RegExBase below for more
- // information.
-
- template
- class RegExBaseHelper : protected ITEM_TRAITS
- {
- public:
- typedef typename ITEM_TRAITS::RegexIterator RegexIterator;
- typedef typename ITEM_TRAITS::InputIterator InputIterator;
-
- typedef typename ITEM_TRAITS::MatchFlags MatchFlags;
- static const MatchFlags DefaultMatchFlags = ITEM_TRAITS::DefaultMatchFlags;
-
- // Arguments:
- // regex : marks the start of the regular expression string.
- // regexEnd : marks the end of the regular expression string.
- // input : marks the start of the input string against which regex will be matched.
- // inputEnd : marks the end of the input string.
- // groups : recipient of regular expression groups.
- //
- // Returns true if the regular expression was successfully matched against the input string;
- // otherwise false.
-
- RegExBaseHelper(const RegexIterator& regex,
- const RegexIterator& regexEnd,
- const InputIterator& input,
- const InputIterator& inputEnd,
- GROUP_CONTAINER& groups,
- const MatchFlags flags = DefaultMatchFlags);
-
- // The main entrypoint to RegExBaseHelper, Match will attempt to match the regular expression
- // defined by [regex,regexEnd) against the input defined by [input,inputEnd).
- bool Match()
- { WRAPPER_NO_CONTRACT; return DoMatch(m_regex, m_input); }
-
- protected:
- typedef typename ITEM_TRAITS::Item Item;
- typedef typename ITEM_TRAITS::ItemType ItemType;
-
- // Try to match regex at any point within input, starting with the first character and moving
- // along one at a time until a match is found or the end of the input is encountered, whichever
- // comes first.
- bool DoMatch(
- const RegexIterator& regex,
- InputIterator input);
-
- // Try to match regex starting exactly at input.
- bool DoMatchHere(
- const RegexIterator& regex,
- const InputIterator& input);
-
- // The function returns true if a match is found consisting of zero or more items c followed by a
- // successful match of regex on the remaining input; otherwise false is returned. This is a
- // conservative match, so it starts with trying to match zero items followed by regex,
- // and will then try to match one item followed by regex.
- bool DoMatchStar(
- const Item& c,
- const RegexIterator& regex,
- InputIterator input);
-
- // The function returns true if a match is found consisting of zero or more items c followed by a
- // successful match of regex on the remaining input; otherwise false is returned. This is a
- // greedy match, so it starts with trying to match as many items as it can followed by regex,
- // and on failure will try again with one less items matched.
- bool DoMatchStarEagerly(
- const Item& c,
- const RegexIterator& regex,
- InputIterator input);
-
- // Convenience method.
- Item GetItem(
- const RegexIterator ®ex)
- { WRAPPER_NO_CONTRACT; return ITEM_TRAITS::GetItem(regex, m_regexEnd, m_flags); }
-
- // Convenience method.
- bool MatchItem(
- const Item& c,
- const InputIterator& input)
- { WRAPPER_NO_CONTRACT; return ITEM_TRAITS::MatchItem(c, input, m_inputEnd, m_flags); }
-
- // Declared as protected to prevent direct instantiation.
- RegExBaseHelper()
- {}
-
- RegexIterator m_regex;
- RegexIterator m_regexEnd;
- InputIterator m_input;
- InputIterator m_inputEnd;
- GROUP_CONTAINER& m_groups;
- MatchFlags m_flags;
- };
-
- //---------------------------------------------------------------------------------------------------
- // This method simply stores the end iterators for the regular expression and the input strings, as
- // well as the group collection object and flags, and forwards the call to DoMatch.
-
- template
- RegExBaseHelper::RegExBaseHelper(
- const RegexIterator& regex,
- const RegexIterator& regexEnd,
- const InputIterator& input,
- const InputIterator& inputEnd,
- GROUP_CONTAINER& groups,
- const MatchFlags flags)
- : m_regex(regex),
- m_regexEnd(regexEnd),
- m_input(input),
- m_inputEnd(inputEnd),
- m_groups(groups),
- m_flags(flags)
- { WRAPPER_NO_CONTRACT; }
-
- //---------------------------------------------------------------------------------------------------
- // This method checks if the regular expression starts with a caret, indicating that any match must
- // be anchored at the start of the input string. If such a caret exists, one match attempt is made
- // on the input starting with the first character and the result is returned. If the regex does not
- // start with a caret, the method attempts to match against the input string, starting at the first
- // character and moving one character over for each successive attempt, until a match is found or
- // the end of the input is encountered, whichever comes first.
-
- template
- inline bool
- RegExBaseHelper::DoMatch(
- const RegexIterator& regex,
- InputIterator input)
- {
- WRAPPER_NO_CONTRACT;
-
- if (GetItem(regex).GetType() == ITEM_TRAITS::CARET)
- { // Match must occur from the beginning of the line
- m_groups.OpenGroup(input, m_inputEnd);
- bool res = DoMatchHere(regex+1, input);
- if (!res)
- m_groups.CancelGroup();
- return res;
- }
- else
- { // Match can happen anywhere in the string
- do
- { // Attempt to match against each substring [x,inputEnd) for x = 0...inputEnd
-
- // Begin the group that contains the entire match.
- m_groups.OpenGroup(input, m_inputEnd);
-
- if (DoMatchHere(regex, input))
- { // Success. Note that the entire match group is closed elsewhere on a
- // successful match.
- return true;
- }
-
- // On failure, cancel the group so that it can be reopened on the new substring
- m_groups.CancelGroup();
- } while (input++ != m_inputEnd);
- }
-
- // No successful match found.
- return false;
- }
-
- //-------------------------------------------------------------------------------------------------------
- // This is the main loop, which handles grouping constructs, repetition directives (*, *?, +, +?), and
- // EOL matches ($), delegating all character matching to ITEM_TRAITS::MatchItem
- // The general algorithm is:
- // 1. Get the next item.
- // 2. If the item is a DOLLAR type, check to see if we're at the end of the retular expression and
- // the input string, and if so return success.
- // 3. If the item is a grouping construct, open or close the appropriate group and continue matching.
- // On failure, roll back the grouping change so that subsequent attemts will have correct state.
- // 4. Check to see if the item following the current is a repetition directive, and if so take the
- // appropriate action.
- // 5. Otherwise defer to ITEM_TRAITS::MatchItem and if successful continue to match the remaining
- // regular expression and input string; otherwise return failure.
-
- template
- inline bool
- RegExBaseHelper::DoMatchHere(
- const RegexIterator& regex,
- const InputIterator& input)
- {
- WRAPPER_NO_CONTRACT;
-
- if (regex == m_regexEnd)
- { // Reached the end of the regular expression without ever returning false,
- // implying a successful match. Close the overall match group and return.
- m_groups.CloseGroup(input);
- return true;
- }
-
- Item c0 = GetItem(regex);
- if (c0.GetType() == ITEM_TRAITS::DOLLAR && (c0.GetNext() == m_regexEnd))
- { // Matches EOL if a '$' is encountered at the end of the input.
- m_groups.CloseGroup(input);
- // Success only if we're actually at the end of the input string.
- return input == m_inputEnd;
- }
- if (c0.GetType() == ITEM_TRAITS::PAREN_OPEN)
- { // Encountered an open parenthesis ('('); open a new grouping.
- m_groups.OpenGroup(input, m_inputEnd);
- bool res = DoMatchHere(c0.GetNext(), input);
- if (!res)
- { // If a match fails, there could be further attempts (such as if
- // there is an active repetition matching frame beneath us), so
- // need to cancel the group we just opened so that the grouping
- // structure remains consistent.
- m_groups.CancelGroup();
- }
- return res;
- }
- if (c0.GetType() == ITEM_TRAITS::PAREN_CLOSE)
- { // Close the most recent open grouping.
- COUNT_T i = m_groups.CloseGroup(input);
- bool res = DoMatchHere(c0.GetNext(), input);
- if (!res)
- { // For the same reasons as the need to cancel an opened group
- // explained above, we need to reopen the closed group if a
- // match fails.
- m_groups.ReopenGroup(i, m_inputEnd);
- }
- return res;
- }
-
- if (c0.GetNext() != m_regexEnd)
- { // If there is another item in the regex string following the current one, get
- // it to see if it is a repetition matching directive.
- Item c1 = GetItem(c0.GetNext());
- if (c1.GetType() == ITEM_TRAITS::STAR)
- { // '*' matching directive encountered
- if (c1.GetNext() != m_regexEnd)
- {
- Item c2 = GetItem(c1.GetNext());
- if (c2.GetType() == ITEM_TRAITS::QUESTION_MARK)
- { // conservative matching semantics requested
- return DoMatchStar(c0, c2.GetNext(), input);
- }
- }
- // Eager matching
- return DoMatchStarEagerly(c0, c1.GetNext(), input);
- }
- if (c1.GetType() == ITEM_TRAITS::PLUS)
- { // '+' matching directive encountered.
- if (c1.GetNext() != m_regexEnd)
- {
- Item c2 = GetItem(c1.GetNext());
- if (c2.GetType() == ITEM_TRAITS::QUESTION_MARK)
- { // conservative matching semantics requested
- return MatchItem(c0, input) && DoMatchStar(c0, c2.GetNext(), input+1);
- }
- }
- // Eager matching
- return MatchItem(c0, input) && DoMatchStarEagerly(c0, c1.GetNext(), input+1);
- }
- if (c1.GetType() == ITEM_TRAITS::QUESTION_MARK)
- { // '?' matching directive encountered
- return (MatchItem(c0, input) && DoMatchHere(c1.GetNext(), input+1)) || DoMatchHere(c1.GetNext(), input);
- }
- }
-
- // No special matching semantics encountered, delegate the matching to ITEM_TRAITS::MatchItem
- return MatchItem(c0, input) && DoMatchHere(c0.GetNext(), input+1);
- }
-
- //-------------------------------------------------------------------------------------------------------
- // Conservative '*' repetition matching. This attempts to match zero items c followed by a match of
- // regex. If this fails, attempt to match one item c followed by a match of regex. Repeat until item c
- // does not match or a successful match is found.
-
- template
- inline bool
- RegExBaseHelper::DoMatchStar(
- const Item& c,
- const RegexIterator& regex,
- InputIterator input)
- {
- WRAPPER_NO_CONTRACT;
- CONSISTENCY_CHECK(input != m_inputEnd);
-
- do {
- if (DoMatchHere(regex, input))
- { // A successful match is found!
- return true;
- }
- // No successful match, so try to match one more item and then attempt to match regex on the
- // remaining input.
- } while (input != m_inputEnd && MatchItem(c, input++));
- return false;
- }
-
- //-------------------------------------------------------------------------------------------------------
- // Similar to DoMatchStar above, except this algorithm matches as many items c as possible first followed
- // by regex on the remaining input, and on failure tries again with a match against one less item c
- // followed by regex on the remaining input, and repeats until there are no items c remaining to match
- // and the zero item match followed by a match of regex on the entire remaining input fails. If any of
- // the match attempts succeed, return success.
-
- template
- inline bool
- RegExBaseHelper::DoMatchStarEagerly(
- const Item& c,
- const RegexIterator& regex,
- InputIterator input)
- {
- WRAPPER_NO_CONTRACT;
-
- // Make sure we keep a hold of how far back we can unwind.
- InputIterator inputOrig = input;
-
- // First, determine the maximum number of matches against item c.
- while (input != m_inputEnd && MatchItem(c, input))
- {
- ++input;
- }
-
- do
- { // Work backwards from the maximum number of matches of item c until a match is found
- // or until we have backed right up to the starting value of input (saved in inputOrig),
- // at which time we admit failure.
- if (DoMatchHere(regex, input))
- return true;
- } while (inputOrig != input--);
- return false;
- }
-
-} // namespace imp
-
-//=======================================================================================================
-// Represents a matched group using iterators to denote the string contained by [Begin(),End()).
-
-template
-class Group
-{
-public:
- typedef INPUT_ITERATOR InputIterator;
-
- //
- // Functions for accessing group properties
- //
-
- // Returns the iterator indicating the start of the group
- const InputIterator& Begin() const
- { LIMITED_METHOD_CONTRACT; return m_begin; }
-
- // Returns the iterator indicating the first non-member of the group
- const InputIterator& End() const
- { LIMITED_METHOD_CONTRACT; return m_end; }
-
- // It is possible that m_end - m_begin could be greater than the maximum of COUNT_T. m_end and
- // m_begin are the end and start of a string, so is entirely unlikely to overflow a COUNT_T.
- // Conbined with the fact that this is debug-only code, opting not to replace all COUNT_T
- // uses with SIZE_T.
- COUNT_T Length() const
- { WRAPPER_NO_CONTRACT; return static_cast(m_end - m_begin); }
-
- //
- // Functions used by RegExBaseHelper to create grouping constructs.
- //
-
- Group()
- : m_isClosed(false), m_begin(), m_end()
- { WRAPPER_NO_CONTRACT; }
-
- Group(const InputIterator& start, const InputIterator& end, bool isClosed = false)
- : m_isClosed(isClosed), m_begin(start), m_end(end)
- { WRAPPER_NO_CONTRACT; }
-
- void SetBegin(const InputIterator& start)
- { WRAPPER_NO_CONTRACT; m_begin = start; }
-
- void SetEnd(const InputIterator& end)
- { WRAPPER_NO_CONTRACT; m_end = end; }
-
- bool IsClosed() const
- { LIMITED_METHOD_CONTRACT; return m_isClosed; }
-
- void SetIsClosed(bool isClosed)
- { WRAPPER_NO_CONTRACT; m_isClosed = isClosed; }
-
-protected:
- bool m_isClosed;
- InputIterator m_begin;
- InputIterator m_end;
-};
-
-//=======================================================================================================
-// Represents a generic container of groups, defaulting to using Group as its element
-// type. This container satisfies the method requrements of RegExBase. When a match is successful, the
-// match groups may be accessed using the index operator or the iterators definin the matched groups
-// [Begin(), End()).
-
-template >
-class GroupContainer
-{
-public:
- typedef typename SArray::Iterator Iterator;
-
- //
- // Functions for enumerating groups
- //
-
- GROUP_TYPE & operator[](COUNT_T idx)
- {
- WRAPPER_NO_CONTRACT;
- CONSISTENCY_CHECK(((COUNT_T)(COUNT_T)idx) == idx);
- return m_array[idx];
- }
-
- // Returns an iterator to the first matched group (which is always the string for the
- // entire successfully matched string. Specific groups start at Begin()+1 and continue
- // to End()-1.
- Iterator Begin()
- { WRAPPER_NO_CONTRACT; return m_array.Begin(); }
-
- // Returns the first invalid iterator value.
- Iterator End()
- { WRAPPER_NO_CONTRACT; return m_array.End(); }
-
- //
- COUNT_T Count() const
- { WRAPPER_NO_CONTRACT; return m_array.GetCount(); }
-
- //
- // Functions used by RegExBaseHelper to create grouping constructs.
- //
-
- // Note: OpenGroup takes an end iterator so that the group will have a valid (if possibly
- // incorrect) endpoint in the case that the regular expression has unbalanced grouping
- // parentheses.
- void OpenGroup(const INPUT_ITERATOR& start, const INPUT_ITERATOR& end)
- { WRAPPER_NO_CONTRACT; m_array.Append(GROUP_TYPE(start, end, false)); }
-
- COUNT_T CloseGroup(const INPUT_ITERATOR& end);
-
- void ReopenGroup(COUNT_T i, const INPUT_ITERATOR& end);
-
- void CancelGroup()
- { WRAPPER_NO_CONTRACT; m_array.Delete(m_array.End() - 1); }
-
-private:
- SArray m_array;
-};
-
-//-------------------------------------------------------------------------------------------------------
-// Works backwards from the most recently created group looking for an open group to close. Returns
-// the index of the group that was closed, which is used in the event that a group needs to be
-// reopened.
-
-template
-COUNT_T
-GroupContainer::CloseGroup(
- const INPUT_ITERATOR& end)
-{
- WRAPPER_NO_CONTRACT;
-
- for (COUNT_T i = (COUNT_T)Count(); i > 0; --i)
- {
- if (!m_array[i-1].IsClosed())
- {
- m_array[i-1].SetEnd(end);
- m_array[i-1].SetIsClosed(true);
- return i-1;
- }
- }
-
- _ASSERTE(!"Unmatched grouping constructs!");
- return 0;
-}
-
-//-------------------------------------------------------------------------------------------------------
-// Reopen a group at the given index, using 'end' to overwrite the current end.
-
-template
-void
-GroupContainer::ReopenGroup(
- COUNT_T i,
- const INPUT_ITERATOR& end)
-{
- WRAPPER_NO_CONTRACT;
- CONSISTENCY_CHECK(i > 0 && i < Count());
-
- if (i > 0 && i < Count())
- {
- m_array[i].SetEnd(end);
- m_array[i].SetIsClosed(false);
- }
-}
-
-//=======================================================================================================
-// Empty group container that satisfies the method requirements of RegExBase but has empty bodies. This
-// allows for non-allocating matches when grouping is not required.
-
-template
-class NullGroupContainer
-{
-public:
- void OpenGroup(INPUT_ITERATOR, INPUT_ITERATOR) {}
- COUNT_T CloseGroup(INPUT_ITERATOR) { return 0; }
- void ReopenGroup(COUNT_T, INPUT_ITERATOR) {}
- void CancelGroup() {}
-};
-
-//=======================================================================================================
-// This mini-implementation of regular expression matching supports the
-// following constructs:
-// ^ matches the beginning of the input string
-// $ matches the end of the input string
-// * matches zero or more occurrences of the previous item eagerly
-// *? matches zero or more occurrences of the previous item conservatively
-// + matches 1 or more occurrences of the previous item eagerly
-// +? matches 1 or more occurrences of the previous item conservatively
-// ? matches 0 or 1 occurrences of the previous item
-// ( starts a grouping
-// ) ends a grouping
-//
-// IMPORTANT: These are just anchoring and grouping constructs. See the definition for ItemTraitsBase
-// below for information on the default character classes that are supported. (The intent of
-// this separation is to allow customization of the character classes where required.)
-
-// ITEM_TRAITS provides traits for individual tokens in a regular expression, as well as a mechanism for
-// matching said individual components with the target string. RegexBase derives from ITEM_TRAITS in a
-// protected fashion, and is responsible for providing the following:
-// 1. "RegexIterator" typedef
-// Used as an iterator into the regular expression, and used as arguments to indicate the start
-// and the end of the regular expression string.
-// 2. "InputIterator" typedef
-// Used as an iterator into the input string, and used as arguments to indicate the start
-// and the end of the input string.
-// (NOTE: RegexIterator and InputIterator are often typedef'ed to be the same thing.)
-// 3. "Item" typedef.
-// This will be used with methods GetItem and MatchItem (see below). Item must
-// define the following methods:
-// ItemType GetType() : returns the type of the item. See below for explanation of ItemType
-// const RegexIterator& GetNext() : iterator pointing to the start of the next item.
-// 4. "MatchFlags" typedef, and "static const DefaultMatchFlags" value.
-// Provided for calls to "Match" and "Matches", and passed on to calls "GetItem" and "MatchItem".
-// 5. enum ItemType
-// Defines the following minimum values:
-// DOT
-// CARET
-// DOLLAR
-// STAR
-// QUESTION_MARK
-// PLUS
-// PAREN_OPEN
-// PAREN_CLOSE
-// ItemType may include more values, and may even choose to ignore the above enum types, all of
-// which must be recognized by GetItem and MatchItem (see below).
-// 6. static Item GetItem(const RegexIterator& regex,
-// const RegexIterator& regexEnd,
-// const MatchFlags& flags)
-// This method takes a regular expression iterator and returns the next regular expression
-// element (Item) pointed to by the iterator.
-// 7. static bool MatchItem(const Item& c,
-// const InputIterator& input,
-// const InputIterator& inputEnd,
-// const MatchFlags &flags)
-
-// GROUP_CONTAINER provides functionality for keeping track of regular expression groups. This is a generic
-// argument to Match, and the type of the object must support the following methods:
-// 1. void OpenGroup(const InputIterator& start, const InputIterator& end);
-// Called when a PAREN_OPEN item is encountered.
-// 2. COUNT_T CloseGroup(const InputIterator& end);
-// Called when a PAREN_CLOSE item is encountered. Returns the index of the group that was closed.
-// 3. void ReopenGroup(COUNT_T i, const InputIterator& end);
-// Called when a match following a call to CloseGroup fails, essentially requesting a rollback
-// of the call to CloseGroup.
-// 4. void CancelGroup();
-// Called when a match following a call to OpenGroup fails, essentially requesting a rollback
-// of the call to OpenGroup.
-
-template
-class RegExBase : public ITEM_TRAITS
-{
-public:
- typedef typename ITEM_TRAITS::RegexIterator RegexIterator;
- typedef typename ITEM_TRAITS::InputIterator InputIterator;
-
- // This is a convenience typedef that allows a caller to easily declare a grouping container
- // to be passed to a call to Match. An example would be (see regex_util.h for a definition of
- // SStringRegEx):
- //
- // SString input(SL"Simmons");
- // SStringRegEx::GroupingContainer container;
- // if (SStringRegEx::Match(SL"(Sim+on)", input, container)) {
- // printf("%S", container[1].GetSString(input).GetUnicode());
- // }
- //
- typedef GroupContainer > GroupingContainer;
-
- // Pulls down the typedef for MatchFlags and initialized a static representing the default flags.
- typedef typename ITEM_TRAITS::MatchFlags MatchFlags;
- static const MatchFlags DefaultMatchFlags = ITEM_TRAITS::DefaultMatchFlags;
-
- template
- static bool Match(RegexIterator regex,
- RegexIterator regexEnd,
- InputIterator input,
- InputIterator inputEnd,
- GROUP_CONTAINER& groups,
- MatchFlags flags = DefaultMatchFlags)
- {
- imp::RegExBaseHelper
- re(regex, regexEnd, input, inputEnd, groups, flags);
- return re.Match();
- }
-
- static bool Matches(RegexIterator regex,
- RegexIterator regexEnd,
- InputIterator input,
- InputIterator inputEnd,
- MatchFlags flags = DefaultMatchFlags)
- {
- NullGroupContainer ngc;
- return Match(regex, regexEnd, input, inputEnd, ngc, flags);
- }
-};
-
-//=======================================================================================================
-// In addition to requirements specified on RegExBase, StandardItemTraits provides the following
-// additinal regular expression item types.
-// c matches any literal character c
-// . matches any single character
-// \d any literal digit character
-// \w any alpha character
-// \s any whitespace character
-//
-// Child types of ItemTraitsBase must implement GetItem and MatchItem (see below for full
-// signature requirements). Current child type implementations permit a backslash ('\') to escape
-// special characters ('.', '$', '*', etc.) and allow them to be interpreted as literal characters.
-//
-// This type describes a particular behaviour, but must be subtyped for the particular target character
-// needed, and GetItem and MatchItem must be implemented.
-//
-
-template
-class ItemTraitsBase
-{
-public:
- typedef ITERATOR_TYPE RegexIterator;
- typedef ITERATOR_TYPE InputIterator;
-
- enum MatchFlags
- {
- MF_NONE = 0x00,
- MF_CASE_INSENSITIVE = 0x01 // Match character literals as case insensitive.
- };
-
- static const MatchFlags DefaultMatchFlags = MF_NONE;
-
-protected:
- ItemTraitsBase()
- {}
-
- enum ItemType
- {
- // REQUIRED, as described in RegExBase
- CARET,
- DOLLAR,
- STAR,
- QUESTION_MARK,
- PLUS,
- PAREN_OPEN,
- PAREN_CLOSE,
- // ADDITIONAL
- DOT, // any literal character
- DIGIT, // any digit
- ALPHA, // any alpha character, upper or lower case
- WHITESPACE, // any whitespace character
- NON_WHITESPACE, // any non-whitespace character
- CHARACTER, // a specific literal character
- };
-
- class Item
- {
- public:
- Item(ItemType type, CHAR_TYPE val, const RegexIterator& next)
- : m_type(type), m_val(val), m_next(next)
- { WRAPPER_NO_CONTRACT; }
-
- Item(ItemType type, const RegexIterator& next)
- : m_type(type), m_val(0), m_next(next)
- { WRAPPER_NO_CONTRACT; }
-
- ItemType GetType() const
- { LIMITED_METHOD_CONTRACT; return m_type; }
-
- const RegexIterator& GetNext() const
- { LIMITED_METHOD_CONTRACT; return m_next; }
-
- CHAR_TYPE GetValue() const
- { LIMITED_METHOD_CONTRACT; return m_val; }
-
- protected:
- ItemType m_type;
- CHAR_TYPE m_val;
- RegexIterator m_next;
- };
-
- // All deriving types must add the following methods:
- // static Item GetItem(const RegexIterator& regex, const RegexIterator& regexEnd);
- // static bool MatchItem(const Item& c, const InputIterator& input, const InputIterator& inputEnd);
-};
-
-//=======================================================================================================
-// Implements ItemTraitsBase, provides matching for UNICODE characters.
-//
-// !!!IMPORTANT!!!
-// This is not a complete unicode implementation - only the equivalent of ASCII alpha characters are
-// consider to be part of the alpha set, and this is also the only set on which case insensitive
-// operations will correctly work. If RegEx is moved out of DEBUG ONLY, then this will have to be fixed
-// to properly address these issues.
-// !!!IMPORTANT!!!
-
-template
-class WCHARItemTraits : public ItemTraitsBase
-{
-public:
- typedef ItemTraitsBase PARENT_TYPE;
- typedef typename PARENT_TYPE::RegexIterator RegexIterator;
- typedef typename PARENT_TYPE::InputIterator InputIterator;
- typedef typename PARENT_TYPE::Item Item;
- typedef typename PARENT_TYPE::MatchFlags MatchFlags;
-
- static Item GetItem(const RegexIterator& regex, const RegexIterator& regexEnd, MatchFlags flags);
- static bool MatchItem(const Item& c, const InputIterator& input, const InputIterator& inputEnd, MatchFlags flags);
-
-protected:
- WCHARItemTraits()
- {}
-
-private:
- static inline bool IS_UPPER_A_TO_Z(WCHAR x)
- { WRAPPER_NO_CONTRACT; return (((x) >= W('A')) && ((x) <= W('Z'))); }
-
- static inline bool IS_LOWER_A_TO_Z(WCHAR x)
- { WRAPPER_NO_CONTRACT; return (((x) >= W('a')) && ((x) <= W('z'))); }
-
- static inline WCHAR UPCASE(WCHAR x)
- { WRAPPER_NO_CONTRACT; return (IS_LOWER_A_TO_Z(x) ? ((x) - W('a') + W('A')) : (x)); }
-
- static inline WCHAR DOWNCASE(WCHAR x)
- { WRAPPER_NO_CONTRACT; return (IS_UPPER_A_TO_Z(x) ? ((x) - W('A') + W('a')) : (x)); }
-
- static bool MatchCharacter(WCHAR c1, WCHAR c2, MatchFlags flags)
- { WRAPPER_NO_CONTRACT; return (flags & PARENT_TYPE::MF_CASE_INSENSITIVE) ? (DOWNCASE(c1) == DOWNCASE(c2)) : (c1 == c2); }
-};
-
-//-------------------------------------------------------------------------------------------------------
-// Reads the next item from regex, recognizing special characters outlined in ItemTraitsBase.
-
-template
-typename WCHARItemTraits::Item
-WCHARItemTraits::GetItem(
- const RegexIterator& regex,
- const RegexIterator& regexEnd,
- MatchFlags flags)
-{
- WRAPPER_NO_CONTRACT;
-
- if (*regex == W('\\'))
- {
- const RegexIterator regexNext = regex+1;
- if (regexNext == regexEnd)
- return Item(PARENT_TYPE::CHARACTER, W('\\'), regexNext);
- if (*regexNext == W('d'))
- return Item(PARENT_TYPE::DIGIT, regexNext+1);
- if (*regexNext == W('w'))
- return Item(PARENT_TYPE::ALPHA, regexNext+1);
- if (*regexNext == W('s'))
- return Item(PARENT_TYPE::WHITESPACE, regexNext+1);
- if (*regexNext == W('S'))
- return Item(PARENT_TYPE::NON_WHITESPACE, regexNext+1);
- return Item(PARENT_TYPE::CHARACTER, *regexNext, regexNext+1);
- }
- else if (*regex == W('.'))
- return Item(PARENT_TYPE::DOT, W('.'), regex+1);
- else if (*regex == W('^'))
- return Item(PARENT_TYPE::CARET, W('^'), regex+1);
- else if (*regex == W('$'))
- return Item(PARENT_TYPE::DOLLAR, W('$'), regex+1);
- else if (*regex == W('*'))
- return Item(PARENT_TYPE::STAR, W('*'), regex+1);
- else if (*regex == W('?'))
- return Item(PARENT_TYPE::QUESTION_MARK, W('?'), regex+1);
- else if (*regex == W('+'))
- return Item(PARENT_TYPE::PLUS, W('+'), regex+1);
- else if (*regex == W('('))
- return Item(PARENT_TYPE::PAREN_OPEN, W('('), regex+1);
- else if (*regex == W(')'))
- return Item(PARENT_TYPE::PAREN_CLOSE, W(')'), regex+1);
- else
- return Item(PARENT_TYPE::CHARACTER, *regex, regex + 1);
-}
-
-//-------------------------------------------------------------------------------------------------------
-// Returns true if the next character point to by input matches the character class described by c.
-
-template
-bool
-WCHARItemTraits::MatchItem(
- const Item& c,
- const InputIterator& input,
- const InputIterator& inputEnd,
- MatchFlags flags)
-{
- WRAPPER_NO_CONTRACT;
-
- if (c.GetType() == PARENT_TYPE::DIGIT)
- return *input >= W('0') && *input <= W('9');
- else if (c.GetType() == PARENT_TYPE::ALPHA)
- return (*input >= W('a') && *input <= W('z')) || (*input >= W('A') && *input <= W('Z'));
- else if (c.GetType() == PARENT_TYPE::WHITESPACE)
- return *input == W(' ') || *input == W('\t');
- else if (c.GetType() == PARENT_TYPE::NON_WHITESPACE)
- return !(*input == W(' ') || *input == W('\t'));
- else
- return c.GetType() == PARENT_TYPE::DOT || MatchCharacter(c.GetValue(), *input, flags);
-}
-
-//=======================================================================================================
-// Implements ItemTraitsBase, provides matching for ASCII (*not* UTF8) characters.
-
-template
-class CHARItemTraits : public ItemTraitsBase
-{
-public:
- typedef ItemTraitsBase PARENT_TYPE;
- typedef typename PARENT_TYPE::RegexIterator RegexIterator;
- typedef typename PARENT_TYPE::InputIterator InputIterator;
- typedef typename PARENT_TYPE::Item Item;
- typedef typename PARENT_TYPE::MatchFlags MatchFlags;
-
- static Item GetItem(const RegexIterator& regex, const RegexIterator& regexEnd, MatchFlags flags);
- static bool MatchItem(const Item& c, const InputIterator& input, const InputIterator& inputEnd, MatchFlags flags);
-
-protected:
- CHARItemTraits()
- {}
-
-private:
- static inline bool IS_UPPER_A_TO_Z(CHAR x)
- { WRAPPER_NO_CONTRACT; return (((x) >= 'A') && ((x) <= 'Z')); }
-
- static inline bool IS_LOWER_A_TO_Z(CHAR x)
- { WRAPPER_NO_CONTRACT; return (((x) >= 'a') && ((x) <= 'z')); }
-
- static inline CHAR UPCASE(CHAR x)
- { WRAPPER_NO_CONTRACT; return (IS_LOWER_A_TO_Z(x) ? ((x) - 'a' + 'A') : (x)); }
-
- static inline CHAR DOWNCASE(CHAR x)
- { WRAPPER_NO_CONTRACT; return (IS_UPPER_A_TO_Z(x) ? ((x) - 'A' + 'a') : (x)); }
-
- static bool MatchCharacter(CHAR c1, CHAR c2, MatchFlags flags)
- { WRAPPER_NO_CONTRACT; return (flags & PARENT_TYPE::MF_CASE_INSENSITIVE) ? (DOWNCASE(c1) == DOWNCASE(c2)) : (c1 == c2); }
-};
-
-//-------------------------------------------------------------------------------------------------------
-// Reads the next item from regex, recognizing special characters outlined in ItemTraitsBase.
-
-template
-typename CHARItemTraits::Item
-CHARItemTraits::GetItem(
- const RegexIterator& regex,
- const RegexIterator& regexEnd,
- MatchFlags flags)
-{
- WRAPPER_NO_CONTRACT;
-
- if (*regex == '\\')
- {
- const RegexIterator regexNext = regex+1;
- if (regexNext == regexEnd)
- return Item(PARENT_TYPE::CHARACTER, W('\\'), regexNext);
- if (*regexNext == 'd')
- return Item(PARENT_TYPE::DIGIT, regexNext+1);
- if (*regexNext == 'w')
- return Item(PARENT_TYPE::ALPHA, regexNext+1);
- if (*regexNext == 's')
- return Item(PARENT_TYPE::WHITESPACE, regexNext+1);
- return Item(PARENT_TYPE::CHARACTER, *regexNext, regexNext+1);
- }
- else if (*regex == '.')
- return Item(PARENT_TYPE::DOT, '.', regex+1);
- else if (*regex == '^')
- return Item(PARENT_TYPE::CARET, '^', regex+1);
- else if (*regex == '$')
- return Item(PARENT_TYPE::DOLLAR, '$', regex+1);
- else if (*regex == '*')
- return Item(PARENT_TYPE::STAR, '*', regex+1);
- else if (*regex == '?')
- return Item(PARENT_TYPE::QUESTION_MARK, '?', regex+1);
- else if (*regex == '+')
- return Item(PARENT_TYPE::PLUS, '+', regex+1);
- else if (*regex == '(')
- return Item(PARENT_TYPE::PAREN_OPEN, '(', regex+1);
- else if (*regex == ')')
- return Item(PARENT_TYPE::PAREN_CLOSE, ')', regex+1);
- else
- return Item(PARENT_TYPE::CHARACTER, *regex, regex + 1);
-}
-
-//-------------------------------------------------------------------------------------------------------
-// Returns true if the next character point to by input matches the character class described by c.
-
-template
-bool
-CHARItemTraits::MatchItem(
- const Item& c,
- const InputIterator& input,
- const InputIterator& inputEnd,
- MatchFlags flags)
-{
- WRAPPER_NO_CONTRACT;
-
- if (c.GetType() == PARENT_TYPE::DIGIT)
- return *input >= W('0') && *input <= W('9');
- else if (c.GetType() == PARENT_TYPE::ALPHA)
- return (*input >= W('a') && *input <= W('z')) || (*input >= W('A') && *input <= W('Z'));
- else if (c.GetType() == PARENT_TYPE::WHITESPACE)
- return *input == W(' ') || *input == W('\t');
- else
- return c.GetType() == PARENT_TYPE::DOT || MatchCharacter(c.GetValue(), *input, flags);
-}
-
-} /* namespace regex */
-} /* namespace clr */
-
-#endif // _DEBUG
-
-#endif // _REGEX_BASE_H_
diff --git a/src/coreclr/inc/regex_util.h b/src/coreclr/inc/regex_util.h
deleted file mode 100644
index d96c7423198b26..00000000000000
--- a/src/coreclr/inc/regex_util.h
+++ /dev/null
@@ -1,208 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-// See regex_base.h for more information.
-//
-// This header creates some concrete instantiations of RegExBase for commonly used scenarios. In
-// particular, basic regular expression matching base on the regular expression language described in
-// clr::regex::ItemTraitsBase (found in regex_base.h) is instantiated for use with SString, ASCII and
-// UNICODE strings (clr::regex::SStringRegex, clr::regex::WSTRRegEx, and clr::regex::STRRegEx
-// respectively). Each type definition includes an example of its use.
-//
-
-//
-
-#ifndef _REGEX_UTIL_H_
-#define _REGEX_UTIL_H_
-
-#ifndef MODE_ANY
-#define MODE_ANY
-#endif
-
-#include "regex_base.h"
-
-#ifdef _DEBUG
-
-namespace clr
-{
-namespace regex
-{
-
-//=======================================================================================================
-// Derives from Group to provide two additional convenience methods (GetSString variants).
-
-class SStringGroup : public Group
-{
-public:
- SStringGroup()
- : Group()
- { WRAPPER_NO_CONTRACT; }
-
- SStringGroup(const InputIterator& _start, const InputIterator& _end, bool _isClosed = false)
- : Group(_start, _end, _isClosed)
- { WRAPPER_NO_CONTRACT; }
-
- // Since SStrings constructed from ranges require the original source string, this is a required
- // input argument. Returns the input substring that matches the corresponding grouping.
- SString GetSString(const SString& src)
- { WRAPPER_NO_CONTRACT; return SString(src, Begin(), End()); }
-
- // Since SStrings constructed from ranges require the original source string, this is a required
- // input argument. This version takes a target SString as a buffer, and also returns this buffer
- // as a reference. Returns the input substring that matches the corresponding grouping.
- SString& GetSString(const SString& src, SString& tgt)
- { WRAPPER_NO_CONTRACT; tgt.Set(src, Begin(), End()); return tgt; }
-};
-
-//=======================================================================================================
-typedef WCHARItemTraits SStringItemTraits;
-
-//=======================================================================================================
-// Regular expression matching with SStrings.
-//
-// Here is an example of how to use SStringRegEx with grouping enabled.
-//
-// using namespace clr::regex;
-// SString input(SL"Simmons"); // usually this is derived from some variable source
-// SStringRegEx::GroupingContainer container;
-// if (SStringRegEx::Match(SL"(Sim+on)", input, container)) {
-// printf("%S", container[1].GetSString(input).GetUnicode());
-// }
-//
-// This sample should result in "Simmon" being printed.
-
-
-class SStringRegEx : public RegExBase
-{
- typedef RegExBase PARENT_TYPE;
-
-public:
- using PARENT_TYPE::Match;
- using PARENT_TYPE::Matches;
-
- typedef PARENT_TYPE::InputIterator InputIterator;
-
- typedef GroupContainer GroupingContainer;
-
- static bool Match(
- const SString& regex,
- const SString& input,
- GroupingContainer& groups,
- MatchFlags flags = DefaultMatchFlags)
- {
- WRAPPER_NO_CONTRACT;
- return Match(regex.Begin(), regex.End(), input.Begin(), input.End(), groups, flags);
- }
-
- static bool Matches(
- const SString& regex,
- const SString& input,
- MatchFlags flags = DefaultMatchFlags)
- {
- WRAPPER_NO_CONTRACT;
- return Matches(regex.Begin(), regex.End(), input.Begin(), input.End(), flags);
- }
-
-};
-
-//=======================================================================================================
-// Regular expression matching with UNICODE strings.
-//
-// Here is an example of how to use WSTRRegEx to match against a null-terminated string without grouping.
-//
-// using namespace clr::regex;
-// LPCWSTR input = L"Simmons";
-// if (WSTRRegEx::Matches(L"Sim+on", input))
-// printf("Match succeeded");
-// else
-// printf("Match failed");
-//
-// This sample should result in "Match succeeded" being printed.
-
-class WSTRRegEx : public RegExBase >
-{
- typedef RegExBase > PARENT_TYPE;
-
-public:
- using PARENT_TYPE::Match;
- using PARENT_TYPE::Matches;
-
- static bool Match(
- LPCWSTR regex,
- LPCWSTR input,
- GroupingContainer& groups,
- MatchFlags flags = DefaultMatchFlags)
- {
- WRAPPER_NO_CONTRACT;
- return Match(regex, regex + wcslen(regex), input, input + wcslen(input), groups, flags);
- }
-
- static bool Matches(
- LPCWSTR regex,
- LPCWSTR input,
- MatchFlags flags = DefaultMatchFlags)
- {
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- return Matches(regex, regex + wcslen(regex), input, input + wcslen(input), flags);
- }
-};
-
-//=======================================================================================================
-// Regular expression matching with ASCII strings.
-//
-// Here is an example of how to use STRRegEx to match against a substring based on begin and end range
-// pointers, with grouping disabled and case insensitivity enabled.
-//
-// using namespace clr::regex;
-// LPCSTR input = "123Simmons456";
-// if (STRRegEx::Matches("Sim+on", input+3, input+10, STRRegEx::MF_CASE_INSENSITIVE))
-// printf("Match succeeded");
-// else
-// printf("Match failed");
-//
-// This sample should result in "Match succeeded" being printed.
-
-class STRRegEx : public RegExBase >
-{
- typedef RegExBase > PARENT_TYPE;
-
-public:
- using PARENT_TYPE::Match;
- using PARENT_TYPE::Matches;
-
- static bool Match(
- LPCSTR regex,
- LPCSTR input,
- GroupingContainer& groups,
- MatchFlags flags = DefaultMatchFlags)
- {
- WRAPPER_NO_CONTRACT;
- return Match(regex, regex + strlen(regex), input, input + strlen(input), groups, flags);
- }
-
- static bool Matches(
- LPCSTR regex,
- LPCSTR input,
- MatchFlags flags = DefaultMatchFlags)
- {
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- } CONTRACTL_END;
-
- return Matches(regex, regex + strlen(regex), input, input + strlen(input), flags);
- }
-};
-
-} // namespace regex
-} // namespace clr
-
-#endif // _DEBUG
-
-#endif // _REGEX_UTIL_H_
diff --git a/src/coreclr/inc/sstring.h b/src/coreclr/inc/sstring.h
index 5f5eeb1078e005..3145d1b1e30de4 100644
--- a/src/coreclr/inc/sstring.h
+++ b/src/coreclr/inc/sstring.h
@@ -105,16 +105,11 @@ class EMPTY_BASES_DECL SString : private SBuffer
protected:
class Index;
- class UIndex;
friend class Index;
- friend class UIndex;
public:
- // UIterator is character-level assignable.
- class UIterator;
-
// CIterators/Iterator'string must be modified by SString APIs.
class CIterator;
class Iterator;
@@ -173,6 +168,7 @@ class EMPTY_BASES_DECL SString : private SBuffer
void SetASCII(const ASCII *string);
void SetUTF8(const UTF8 *string);
void SetANSI(const ANSI *string);
+ void SetAndConvertToUTF8(const WCHAR* string);
// Set this string to a copy of the first count chars of the given string
void Set(const WCHAR *string, COUNT_T count);
@@ -325,53 +321,6 @@ class EMPTY_BASES_DECL SString : private SBuffer
// CIterator and Iterator are cheap to create, but allow only read-only
// access to the string.
//
- // UIterator forces a unicode conversion, but allows
- // assignment to individual string characters. They are also a bit more
- // efficient once created.
-
- // ------------------------------------------------------------------
- // UIterator:
- // ------------------------------------------------------------------
-
- protected:
-
- class EMPTY_BASES_DECL UIndex : public SBuffer::Index
- {
- friend class SString;
- friend class Indexer;
-
- protected:
-
- UIndex();
- UIndex(SString *string, SCOUNT_T index);
- WCHAR &GetAt(SCOUNT_T delta) const;
- void Skip(SCOUNT_T delta);
- SCOUNT_T Subtract(const UIndex &i) const;
- CHECK DoCheck(SCOUNT_T delta) const;
-
- WCHAR *GetUnicode() const;
- };
-
- public:
-
- class EMPTY_BASES_DECL UIterator : public UIndex, public Indexer
- {
- friend class SString;
-
- public:
- UIterator()
- {
- }
-
- UIterator(SString *string, int index)
- : UIndex(string, index)
- {
- }
- };
-
- UIterator BeginUnicode();
- UIterator EndUnicode();
-
// For CIterator & Iterator, we try our best to iterate the string without
// modifying it. (Currently, we do require an ASCII or Unicode string
// for simple WCHAR retrival, but you could imagine being more flexible
@@ -544,17 +493,15 @@ class EMPTY_BASES_DECL SString : private SBuffer
// SString *s = ...;
// {
// StackScratchBuffer buffer;
- // const UTF8 *utf8 = s->GetUTF8(buffer);
- // CallFoo(utf8);
+ // const ANSI *ansi = s->GetANSI(buffer);
+ // CallFoo(ansi);
// }
// // No more pointers to returned buffer allowed.
-
- const UTF8 *GetUTF8(AbstractScratchBuffer &scratch) const;
- const UTF8 *GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const;
const ANSI *GetANSI(AbstractScratchBuffer &scratch) const;
- // Used when the representation is known, throws if the representation doesn't match
- const UTF8 *GetUTF8NoConvert() const;
+ // You can always get a UTF8 string. This will force a conversion
+ // if necessary.
+ const UTF8 *GetUTF8() const;
// Converts/copies into the given output string
void ConvertToUnicode(SString &dest) const;
@@ -779,6 +726,7 @@ class EMPTY_BASES_DECL SString : private SBuffer
void ConvertASCIIToUnicode(SString &dest) const;
void ConvertToUnicode() const;
void ConvertToUnicode(const CIterator &i) const;
+ void ConvertToUTF8() const;
const SString &GetCompatibleString(const SString &s, SString &scratch) const;
const SString &GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const;
diff --git a/src/coreclr/inc/sstring.inl b/src/coreclr/inc/sstring.inl
index 03fc26fe9666cf..0d48c5a181db41 100644
--- a/src/coreclr/inc/sstring.inl
+++ b/src/coreclr/inc/sstring.inl
@@ -651,6 +651,25 @@ inline const WCHAR *SString::GetUnicode() const
SS_RETURN GetRawUnicode();
}
+// Get a const pointer to the internal buffer as a UTF8 string.
+inline const UTF8 *SString::GetUTF8() const
+{
+ SS_CONTRACT(const UTF8 *)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UTF8)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUTF8();
+
+ SS_RETURN GetRawUTF8();
+}
+
// Normalize the string to unicode. This will make many operations nonfailing.
inline void SString::Normalize() const
{
@@ -1906,44 +1925,6 @@ inline void SString::ConvertToIteratable() const
SS_RETURN;
}
-//-----------------------------------------------------------------------------
-// Create iterators on the string.
-//-----------------------------------------------------------------------------
-
-inline SString::UIterator SString::BeginUnicode()
-{
- SS_CONTRACT(SString::UIterator)
- {
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(this));
- SS_POSTCONDITION(CheckValue(RETVAL));
- THROWS;
- }
- SS_CONTRACT_END;
-
- ConvertToUnicode();
- EnsureWritable();
-
- SS_RETURN UIterator(this, 0);
-}
-
-inline SString::UIterator SString::EndUnicode()
-{
- SS_CONTRACT(SString::UIterator)
- {
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(this));
- SS_POSTCONDITION(CheckValue(RETVAL));
- THROWS;
- }
- SS_CONTRACT_END;
-
- ConvertToUnicode();
- EnsureWritable();
-
- SS_RETURN UIterator(this, GetCount());
-}
-
//-----------------------------------------------------------------------------
// Create CIterators on the string.
//-----------------------------------------------------------------------------
@@ -2135,74 +2116,6 @@ inline WCHAR SString::Index::operator[](int index) const
return *(WCHAR*)&GetAt(index);
}
-//-----------------------------------------------------------------------------
-// Iterator support routines
-//-----------------------------------------------------------------------------
-
-inline SString::UIndex::UIndex()
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-inline SString::UIndex::UIndex(SString *string, SCOUNT_T index)
- : SBuffer::Index(string, index*sizeof(WCHAR))
-{
- SS_CONTRACT_VOID
- {
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(string));
- PRECONDITION(string->IsRepresentation(REPRESENTATION_UNICODE));
- PRECONDITION(DoCheck(0));
- SS_POSTCONDITION(CheckPointer(this));
- NOTHROW;
- CANNOT_TAKE_LOCK;
- }
- SS_CONTRACT_END;
-
- SS_RETURN;
-}
-
-inline WCHAR &SString::UIndex::GetAt(SCOUNT_T delta) const
-{
- LIMITED_METHOD_CONTRACT;
-
- return ((WCHAR*)m_ptr)[delta];
-}
-
-inline void SString::UIndex::Skip(SCOUNT_T delta)
-{
- LIMITED_METHOD_CONTRACT;
-
- m_ptr += delta * sizeof(WCHAR);
-}
-
-inline SCOUNT_T SString::UIndex::Subtract(const UIndex &i) const
-{
- WRAPPER_NO_CONTRACT;
-
- return (SCOUNT_T) (GetUnicode() - i.GetUnicode());
-}
-
-inline CHECK SString::UIndex::DoCheck(SCOUNT_T delta) const
-{
- CANNOT_HAVE_CONTRACT;
-#if _DEBUG
- const SString *string = (const SString *) GetContainerDebug();
-
- CHECK(GetUnicode() + delta >= string->GetRawUnicode());
- CHECK(GetUnicode() + delta <= string->GetRawUnicode() + string->GetCount());
-#endif
-
- CHECK_OK;
-}
-
-inline WCHAR *SString::UIndex::GetUnicode() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return (WCHAR*) m_ptr;
-}
-
//-----------------------------------------------------------------------------
// Opaque scratch buffer class routines
//-----------------------------------------------------------------------------
diff --git a/src/coreclr/inc/static_assert.h b/src/coreclr/inc/static_assert.h
index e344e83baca554..67336e1290394d 100644
--- a/src/coreclr/inc/static_assert.h
+++ b/src/coreclr/inc/static_assert.h
@@ -14,12 +14,6 @@
#ifndef __STATIC_ASSERT_H__
#define __STATIC_ASSERT_H__
-// static_assert( cond, msg ) is now a compiler-supported intrinsic in Dev10 C++ compiler.
-// Replaces previous uses of STATIC_ASSERT_MSG and COMPILE_TIME_ASSERT_MSG.
-
-// Replaces previous uses of CPP_ASSERT
-#define static_assert_n( n, cond ) static_assert( cond, #cond )
-
// Replaces previous uses of C_ASSERT and COMPILE_TIME_ASSERT
#define static_assert_no_msg( cond ) static_assert( cond, #cond )
diff --git a/src/coreclr/inc/switches.h b/src/coreclr/inc/switches.h
index d3fe1d636440dd..9ec7e3cf05efdb 100644
--- a/src/coreclr/inc/switches.h
+++ b/src/coreclr/inc/switches.h
@@ -53,7 +53,7 @@
#if defined(TARGET_X86) || defined(TARGET_ARM)
#define USE_LAZY_PREFERRED_RANGE 0
-#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64)
+#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64) || defined(TARGET_POWERPC64)
#if defined(HOST_UNIX)
// In PAL we have a smechanism that reserves memory on start up that is
diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h
index 7191833998d4d3..f9444ab439ae6d 100644
--- a/src/coreclr/inc/utilcode.h
+++ b/src/coreclr/inc/utilcode.h
@@ -3808,14 +3808,6 @@ inline bool FitsInRel28(INT64 val64)
return (val64 >= -0x08000000LL) && (val64 < 0x08000000LL);
}
-//*****************************************************************************
-// Splits a command line into argc/argv lists, using the VC7 parsing rules.
-// This functions interface mimics the CommandLineToArgvW api.
-// If function fails, returns NULL.
-// If function suceeds, call delete [] on return pointer when done.
-//*****************************************************************************
-LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs);
-
//
// TEB access can be dangerous when using fibers because a fiber may
// run on multiple threads. If the TEB pointer is retrieved and saved
@@ -3840,11 +3832,6 @@ class ClrTeb
return (void *)(size_t)GetCurrentThreadId();
}
- static void* InvalidFiberPtrId()
- {
- return NULL;
- }
-
static void* GetStackBase()
{
return PAL_GetStackBase();
@@ -3877,33 +3864,12 @@ class ClrTeb
return NtCurrentTeb()->NtTib.StackLimit;
}
- // Please don't start to use this method unless you absolutely have to.
- // The reason why this is added is for WIN64 to support LEGACY PE-style TLS
- // variables. On X86 it is supported by the JIT compilers themselves. On
- // WIN64 we build more logic into the JIT helper for accessing fields.
- static void* GetLegacyThreadLocalStoragePointer()
- {
- LIMITED_METHOD_CONTRACT;
- return NtCurrentTeb()->ThreadLocalStoragePointer;
- }
-
static void* GetOleReservedPtr()
{
LIMITED_METHOD_CONTRACT;
return NtCurrentTeb()->ReservedForOle;
}
- static void* GetProcessEnvironmentBlock()
- {
- LIMITED_METHOD_CONTRACT;
- return NtCurrentTeb()->ProcessEnvironmentBlock;
- }
-
-
- static void* InvalidFiberPtrId()
- {
- return (void*) 1;
- }
#endif // HOST_UNIX
};
diff --git a/src/coreclr/inc/volatile.h b/src/coreclr/inc/volatile.h
index dcfc8af4b329ee..5f3f7491facef0 100644
--- a/src/coreclr/inc/volatile.h
+++ b/src/coreclr/inc/volatile.h
@@ -68,8 +68,8 @@
#error The Volatile type is currently only defined for Visual C++ and GNU C++
#endif
-#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_S390X)
-#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, or S390X CPUs
+#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_S390X) && !defined(HOST_POWERPC64)
+#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, PPC64LE, or S390X CPUs
#endif
#if defined(__GNUC__)
diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt
index 4bc1693e8dfb7c..3410747d021eb8 100644
--- a/src/coreclr/jit/CMakeLists.txt
+++ b/src/coreclr/jit/CMakeLists.txt
@@ -48,6 +48,9 @@ function(create_standalone_jit)
elseif(TARGETDETAILS_ARCH STREQUAL "s390x")
set(JIT_ARCH_SOURCES ${JIT_S390X_SOURCES})
set(JIT_ARCH_HEADERS ${JIT_S390X_HEADERS})
+ elseif(TARGETDETAILS_ARCH STREQUAL "ppc64le")
+ set(JIT_ARCH_SOURCES ${JIT_POWERPC64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_POWERPC64_HEADERS})
elseif(TARGETDETAILS_ARCH STREQUAL "loongarch64")
set(JIT_ARCH_SOURCES ${JIT_LOONGARCH64_SOURCES})
set(JIT_ARCH_HEADERS ${JIT_LOONGARCH64_HEADERS})
@@ -241,6 +244,10 @@ set( JIT_S390X_SOURCES
# Not supported as JIT target
)
+set( JIT_POWERPC64_SOURCES
+ # Not supported as JIT target
+)
+
set( JIT_LOONGARCH64_SOURCES
codegenloongarch64.cpp
emitloongarch64.cpp
@@ -396,6 +403,10 @@ set ( JIT_S390X_HEADERS
# Not supported as JIT target
)
+set ( JIT_POWERPC64_HEADERS
+ # Not supported as JIT target
+)
+
set( JIT_LOONGARCH64_HEADERS
emitloongarch64.h
emitfmtsloongarch64.h
@@ -442,6 +453,9 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
elseif(CLR_CMAKE_TARGET_ARCH_S390X)
set(JIT_ARCH_SOURCES ${JIT_S390X_SOURCES})
set(JIT_ARCH_HEADERS ${JIT_S390X_HEADERS})
+elseif(CLR_CMAKE_TARGET_ARCH_POWERPC64)
+ set(JIT_ARCH_SOURCES ${JIT_POWERPC64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_POWERPC64_HEADERS})
elseif(CLR_CMAKE_TARGET_ARCH_LOONGARCH64)
set(JIT_ARCH_SOURCES ${JIT_LOONGARCH64_SOURCES})
set(JIT_ARCH_HEADERS ${JIT_LOONGARCH64_HEADERS})
@@ -600,13 +614,13 @@ if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)
if (CLR_CMAKE_TARGET_UNIX)
- if (NOT ARCH_TARGET_NAME STREQUAL s390x AND NOT ARCH_TARGET_NAME STREQUAL armv6)
+ if (NOT ARCH_TARGET_NAME STREQUAL s390x AND NOT ARCH_TARGET_NAME STREQUAL armv6 AND NOT ARCH_TARGET_NAME STREQUAL ppc64le)
if(CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARM64)
install_clr(TARGETS clrjit_universal_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit)
else()
install_clr(TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit)
endif()
- endif(NOT ARCH_TARGET_NAME STREQUAL s390x AND NOT ARCH_TARGET_NAME STREQUAL armv6)
+ endif(NOT ARCH_TARGET_NAME STREQUAL s390x AND NOT ARCH_TARGET_NAME STREQUAL armv6 AND NOT ARCH_TARGET_NAME STREQUAL ppc64le)
endif()
if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_PGO_INSTRUMENT)
diff --git a/src/coreclr/jit/ClrJit.PAL.exports b/src/coreclr/jit/ClrJit.PAL.exports
index 2625e98bc421e7..e4e6064db84e89 100644
--- a/src/coreclr/jit/ClrJit.PAL.exports
+++ b/src/coreclr/jit/ClrJit.PAL.exports
@@ -1,4 +1,5 @@
getJit
jitStartup
getLikelyClasses
+getLikelyMethods
jitBuildString
diff --git a/src/coreclr/jit/ClrJit.exports b/src/coreclr/jit/ClrJit.exports
index c6a22db4cae403..5430f7b165929d 100644
--- a/src/coreclr/jit/ClrJit.exports
+++ b/src/coreclr/jit/ClrJit.exports
@@ -5,4 +5,5 @@ EXPORTS
getJit
jitStartup
getLikelyClasses
+ getLikelyMethods
jitBuildString
diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h
index 520315b89f82a0..2549835cefbf8f 100644
--- a/src/coreclr/jit/ICorJitInfo_API_names.h
+++ b/src/coreclr/jit/ICorJitInfo_API_names.h
@@ -10,6 +10,7 @@ DEF_CLR_API(setMethodAttribs)
DEF_CLR_API(getMethodSig)
DEF_CLR_API(getMethodInfo)
DEF_CLR_API(canInline)
+DEF_CLR_API(beginInlining)
DEF_CLR_API(reportInliningDecision)
DEF_CLR_API(canTailCall)
DEF_CLR_API(reportTailCallDecision)
diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
index e65d1be26b3a0e..f6a7b39b1590aa 100644
--- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
+++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
@@ -69,6 +69,15 @@ CorInfoInline WrapICorJitInfo::canInline(
return temp;
}
+void WrapICorJitInfo::beginInlining(
+ CORINFO_METHOD_HANDLE inlinerHnd,
+ CORINFO_METHOD_HANDLE inlineeHnd)
+{
+ API_ENTER(beginInlining);
+ wrapHnd->beginInlining(inlinerHnd, inlineeHnd);
+ API_LEAVE(beginInlining);
+}
+
void WrapICorJitInfo::reportInliningDecision(
CORINFO_METHOD_HANDLE inlinerHnd,
CORINFO_METHOD_HANDLE inlineeHnd,
diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp
index ddee8ba3e6e1b4..ccade20df0fbc7 100644
--- a/src/coreclr/jit/assertionprop.cpp
+++ b/src/coreclr/jit/assertionprop.cpp
@@ -1344,12 +1344,12 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
if (op1->gtGetOp2()->IsCnsIntOrI())
{
offset += op1->gtGetOp2()->AsIntCon()->gtIconVal;
- op1 = op1->gtGetOp1();
+ op1 = op1->gtGetOp1()->gtEffectiveVal(/* commaOnly */ true);
}
else if (op1->gtGetOp1()->IsCnsIntOrI())
{
offset += op1->gtGetOp1()->AsIntCon()->gtIconVal;
- op1 = op1->gtGetOp2();
+ op1 = op1->gtGetOp2()->gtEffectiveVal(/* commaOnly */ true);
}
else
{
@@ -1431,9 +1431,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assertion.op2.vn = ValueNumStore::VNForNull();
assertion.op2.u1.iconVal = 0;
assertion.op2.u1.iconFlags = GTF_EMPTY;
-#ifdef TARGET_64BIT
- assertion.op2.u1.iconFlags |= GTF_ASSERTION_PROP_LONG; // Signify that this is really TYP_LONG
-#endif // TARGET_64BIT
}
//
// Are we making an assertion about a local variable?
@@ -1568,13 +1565,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
#endif // TARGET_ARM
assertion.op2.u1.iconVal = op2->AsIntCon()->gtIconVal;
assertion.op2.u1.iconFlags = op2->GetIconHandleFlag();
-#ifdef TARGET_64BIT
- if (op2->TypeGet() == TYP_LONG || op2->TypeGet() == TYP_BYREF)
- {
- assertion.op2.u1.iconFlags |=
- GTF_ASSERTION_PROP_LONG; // Signify that this is really TYP_LONG
- }
-#endif // TARGET_64BIT
}
else if (op2->gtOper == GT_CNS_LNG)
{
@@ -1731,12 +1721,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
assertion.op2.u1.iconFlags = iconFlags;
-#ifdef TARGET_64BIT
- if (op2->AsOp()->gtOp1->TypeGet() == TYP_LONG)
- {
- assertion.op2.u1.iconFlags |= GTF_ASSERTION_PROP_LONG; // Signify that this is really TYP_LONG
- }
-#endif // TARGET_64BIT
}
// JIT case
else if (optIsTreeKnownIntValue(!optLocalAssertionProp, op2, &cnsValue, &iconFlags))
@@ -1749,12 +1733,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
assertion.op2.u1.iconFlags = iconFlags;
-#ifdef TARGET_64BIT
- if (op2->TypeGet() == TYP_LONG)
- {
- assertion.op2.u1.iconFlags |= GTF_ASSERTION_PROP_LONG; // Signify that this is really TYP_LONG
- }
-#endif // TARGET_64BIT
}
else
{
@@ -2047,13 +2025,9 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
case O2K_IND_CNS_INT:
case O2K_CONST_INT:
{
-// The only flags that can be set are those in the GTF_ICON_HDL_MASK, or GTF_ASSERTION_PROP_LONG, which is
-// used to indicate a long constant.
-#ifdef TARGET_64BIT
- assert((assertion->op2.u1.iconFlags & ~(GTF_ICON_HDL_MASK | GTF_ASSERTION_PROP_LONG)) == 0);
-#else
+ // The only flags that can be set are those in the GTF_ICON_HDL_MASK.
assert((assertion->op2.u1.iconFlags & ~GTF_ICON_HDL_MASK) == 0);
-#endif
+
switch (assertion->op1.kind)
{
case O1K_EXACT_TYPE:
@@ -3177,6 +3151,15 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)
if (conValTree != nullptr)
{
+ if (tree->OperIs(GT_LCL_VAR))
+ {
+ if (!optIsProfitableToSubstitute(tree->AsLclVar(), block, conValTree))
+ {
+ // Not profitable to substitute
+ return nullptr;
+ }
+ }
+
// Were able to optimize.
conValTree->gtVNPair = vnPair;
GenTree* sideEffList = optExtractSideEffListFromConst(tree);
@@ -3199,6 +3182,55 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)
}
}
+//------------------------------------------------------------------------------
+// optIsProfitableToSubstitute: Checks if value worth substituting to lcl location
+//
+// Arguments:
+// lcl - lcl to replace with value if profitable
+// lclBlock - Basic block lcl located in
+// value - value we plan to substitute to lcl
+//
+// Returns:
+// False if it's likely not profitable to do substitution, True otherwise
+//
+bool Compiler::optIsProfitableToSubstitute(GenTreeLclVarCommon* lcl, BasicBlock* lclBlock, GenTree* value)
+{
+ // A simple heuristic: If the constant is defined outside of a loop (not far from its head)
+ // and is used inside it - don't propagate.
+
+ // TODO: Extend on more kinds of trees
+ if (!value->OperIs(GT_CNS_VEC, GT_CNS_DBL))
+ {
+ return true;
+ }
+
+ gtPrepareCost(value);
+
+ if ((value->GetCostEx() > 1) && (value->GetCostSz() > 1))
+ {
+ // Try to find the block this constant was originally defined in
+ if (lcl->HasSsaName())
+ {
+ BasicBlock* defBlock = lvaGetDesc(lcl)->GetPerSsaData(lcl->GetSsaNum())->GetBlock();
+ if (defBlock != nullptr)
+ {
+ // Avoid propagating if the weighted use cost is significantly greater than the def cost.
+ // NOTE: this currently does not take "a float living across a call" case into account
+ // where we might end up with spill/restore on ABIs without callee-saved registers
+ const weight_t defBlockWeight = defBlock->getBBWeight(this);
+ const weight_t lclblockWeight = lclBlock->getBBWeight(this);
+
+ if ((defBlockWeight > 0) && ((lclblockWeight / defBlockWeight) >= BB_LOOP_WEIGHT_SCALE))
+ {
+ JITDUMP("Constant propagation inside loop " FMT_BB " is not profitable\n", lclBlock->bbNum);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
//------------------------------------------------------------------------------
// optConstantAssertionProp: Possibly substitute a constant for a local use
//
diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index 88dcb79794fb87..e9a539a2f35fa6 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -526,32 +526,32 @@ enum BasicBlockFlags : unsigned __int64
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
- BBF_BACKWARD_JUMP = MAKE_BBFLAG(24), // BB is surrounded by a backward jump/switch arc
- BBF_RETLESS_CALL = MAKE_BBFLAG(25), // BBJ_CALLFINALLY that will never return (and therefore, won't need a paired
- // BBJ_ALWAYS); see isBBCallAlwaysPair().
- BBF_LOOP_PREHEADER = MAKE_BBFLAG(26), // BB is a loop preheader block
- BBF_COLD = MAKE_BBFLAG(27), // BB is cold
-
- BBF_PROF_WEIGHT = MAKE_BBFLAG(28), // BB weight is computed from profile data
- BBF_IS_LIR = MAKE_BBFLAG(29), // Set if the basic block contains LIR (as opposed to HIR)
- BBF_KEEP_BBJ_ALWAYS = MAKE_BBFLAG(30), // A special BBJ_ALWAYS block, used by EH code generation. Keep the jump kind
- // as BBJ_ALWAYS. Used for the paired BBJ_ALWAYS block following the
- // BBJ_CALLFINALLY block, as well as, on x86, the final step block out of a
- // finally.
- BBF_CLONED_FINALLY_BEGIN = MAKE_BBFLAG(31), // First block of a cloned finally region
-
- BBF_CLONED_FINALLY_END = MAKE_BBFLAG(32), // Last block of a cloned finally region
- BBF_HAS_CALL = MAKE_BBFLAG(33), // BB contains a call
+ BBF_BACKWARD_JUMP = MAKE_BBFLAG(24), // BB is surrounded by a backward jump/switch arc
+ BBF_RETLESS_CALL = MAKE_BBFLAG(25), // BBJ_CALLFINALLY that will never return (and therefore, won't need a paired
+ // BBJ_ALWAYS); see isBBCallAlwaysPair().
+ BBF_LOOP_PREHEADER = MAKE_BBFLAG(26), // BB is a loop preheader block
+ BBF_COLD = MAKE_BBFLAG(27), // BB is cold
+
+ BBF_PROF_WEIGHT = MAKE_BBFLAG(28), // BB weight is computed from profile data
+ BBF_IS_LIR = MAKE_BBFLAG(29), // Set if the basic block contains LIR (as opposed to HIR)
+ BBF_KEEP_BBJ_ALWAYS = MAKE_BBFLAG(30), // A special BBJ_ALWAYS block, used by EH code generation. Keep the jump kind
+ // as BBJ_ALWAYS. Used for the paired BBJ_ALWAYS block following the
+ // BBJ_CALLFINALLY block, as well as, on x86, the final step block out of a
+ // finally.
+ BBF_CLONED_FINALLY_BEGIN = MAKE_BBFLAG(31), // First block of a cloned finally region
+
+ BBF_CLONED_FINALLY_END = MAKE_BBFLAG(32), // Last block of a cloned finally region
+ BBF_HAS_CALL = MAKE_BBFLAG(33), // BB contains a call
BBF_DOMINATED_BY_EXCEPTIONAL_ENTRY = MAKE_BBFLAG(34), // Block is dominated by exceptional entry.
- BBF_BACKWARD_JUMP_TARGET = MAKE_BBFLAG(35), // Block is a target of a backward jump
+ BBF_BACKWARD_JUMP_TARGET = MAKE_BBFLAG(35), // Block is a target of a backward jump
- BBF_PATCHPOINT = MAKE_BBFLAG(36), // Block is a patchpoint
- BBF_HAS_CLASS_PROFILE = MAKE_BBFLAG(37), // BB contains a call needing a class profile
- BBF_PARTIAL_COMPILATION_PATCHPOINT = MAKE_BBFLAG(38), // Block is a partial compilation patchpoint
- BBF_HAS_ALIGN = MAKE_BBFLAG(39), // BB ends with 'align' instruction
- BBF_TAILCALL_SUCCESSOR = MAKE_BBFLAG(40), // BB has pred that has potential tail call
+ BBF_PATCHPOINT = MAKE_BBFLAG(36), // Block is a patchpoint
+ BBF_HAS_HISTOGRAM_PROFILE = MAKE_BBFLAG(37), // BB contains a call needing a histogram profile
+ BBF_PARTIAL_COMPILATION_PATCHPOINT = MAKE_BBFLAG(38), // Block is a partial compilation patchpoint
+ BBF_HAS_ALIGN = MAKE_BBFLAG(39), // BB ends with 'align' instruction
+ BBF_TAILCALL_SUCCESSOR = MAKE_BBFLAG(40), // BB has pred that has potential tail call
- BBF_BACKWARD_JUMP_SOURCE = MAKE_BBFLAG(41), // Block is a source of a backward jump
+ BBF_BACKWARD_JUMP_SOURCE = MAKE_BBFLAG(41), // Block is a source of a backward jump
// The following are sets of flags.
@@ -582,7 +582,7 @@ enum BasicBlockFlags : unsigned __int64
// TODO: Should BBF_RUN_RARELY be added to BBF_SPLIT_GAINED ?
BBF_SPLIT_GAINED = BBF_DONT_REMOVE | BBF_HAS_JMP | BBF_BACKWARD_JUMP | BBF_HAS_IDX_LEN | BBF_HAS_NEWARRAY | BBF_PROF_WEIGHT | \
- BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_CLASS_PROFILE,
+ BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE,
};
inline constexpr BasicBlockFlags operator ~(BasicBlockFlags a)
@@ -918,8 +918,8 @@ struct BasicBlock : private LIR::Range
};
union {
- unsigned bbStkTempsOut; // base# for output stack temps
- int bbClassSchemaIndex; // schema index for class instrumentation
+ unsigned bbStkTempsOut; // base# for output stack temps
+ int bbHistogramSchemaIndex; // schema index for histogram instrumentation
};
#define MAX_XCPTN_INDEX (USHRT_MAX - 1)
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 8646d9191edcf1..c294957a0cd2c8 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -642,6 +642,7 @@ class CodeGen final : public CodeGenInterface
virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value);
virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const;
bool genSaveFpLrWithAllCalleeSavedRegisters;
+ bool genForceFuncletFrameType5;
#endif // TARGET_ARM64
//-------------------------------------------------------------------------
@@ -1335,10 +1336,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genPutStructArgStk(GenTreePutArgStk* treeNode);
- unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset);
- unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset);
- unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset);
- unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset);
+ unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
+ unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
+ unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
+ unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset);
void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset);
void genStoreRegToStackArg(var_types type, regNumber reg, int offset);
void genStructPutArgRepMovs(GenTreePutArgStk* putArgStkNode);
diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp
index 2b9d4be0abc7d9..b13c11c50d6681 100644
--- a/src/coreclr/jit/codegenarm64.cpp
+++ b/src/coreclr/jit/codegenarm64.cpp
@@ -186,6 +186,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog)
JITDUMP("Frame type 5 (save FP/LR at top). #outsz=%d; #framesz=%d; localloc? %s\n",
unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed));
+ assert(genSaveFpLrWithAllCalleeSavedRegisters);
assert((calleeSaveSpOffset == 0) || (calleeSaveSpOffset == REGSIZE_BYTES));
// Restore sp from fp:
@@ -1077,10 +1078,14 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* | incoming arguments |
* +=======================+ <---- Caller's SP
+ * | OSR padding | // If required
+ * |-----------------------|
* | Varargs regs space | // Only for varargs main functions; 64 bytes
* |-----------------------|
* |Callee saved registers | // multiple of 8 bytes
* |-----------------------|
+ * | MonitorAcquired | // 8 bytes; for synchronized methods
+ * |-----------------------|
* | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
* |-----------------------|
* ~ alignment padding ~ // To make the whole frame 16 byte aligned.
@@ -1104,10 +1109,14 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* | incoming arguments |
* +=======================+ <---- Caller's SP
+ * | OSR padding | // If required
+ * |-----------------------|
* | Varargs regs space | // Only for varargs main functions; 64 bytes
* |-----------------------|
* |Callee saved registers | // multiple of 8 bytes
* |-----------------------|
+ * | MonitorAcquired | // 8 bytes; for synchronized methods
+ * |-----------------------|
* | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
* |-----------------------|
* ~ alignment padding ~ // To make the whole frame 16 byte aligned.
@@ -1134,20 +1143,24 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* | incoming arguments |
* +=======================+ <---- Caller's SP
+ * | OSR padding | // If required
+ * |-----------------------|
* | Varargs regs space | // Only for varargs main functions; 64 bytes
* |-----------------------|
* |Callee saved registers | // multiple of 8 bytes
* |-----------------------|
+ * | MonitorAcquired | // 8 bytes; for synchronized methods
+ * |-----------------------|
* | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
* |-----------------------|
* ~ alignment padding ~ // To make the first SP subtraction 16 byte aligned
* |-----------------------|
- * | Saved FP, LR | // 16 bytes
+ * | Saved FP, LR | // 16 bytes <-- SP after first adjustment (points at saved FP)
* |-----------------------|
* ~ alignment padding ~ // To make the whole frame 16 byte aligned (specifically, to 16-byte align the outgoing argument space).
* |-----------------------|
* | Outgoing arg space | // multiple of 8 bytes
- * |-----------------------| <---- Ambient SP
+ * |-----------------------| <---- Ambient SP (SP after second adjustment)
* | | |
* ~ | Stack grows ~
* | | downward |
@@ -1162,7 +1175,7 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* 8 float callee-saved registers v8-v15
* 8 saved integer argument registers x0-x7, if varargs function
* 1 PSP slot
- * 1 alignment slot
+ * 1 alignment slot or monitor acquired slot
* == 30 slots * 8 bytes = 240 bytes.
*
* The outgoing argument size, however, can be very large, if we call a function that takes a large number of
@@ -1198,6 +1211,8 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* | incoming arguments |
* +=======================+ <---- Caller's SP
+ * | OSR padding | // If required
+ * |-----------------------|
* | Varargs regs space | // Only for varargs main functions; 64 bytes
* |-----------------------|
* | Saved LR | // 8 bytes
@@ -1206,6 +1221,8 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* |Callee saved registers | // multiple of 8 bytes
* |-----------------------|
+ * | MonitorAcquired | // 8 bytes; for synchronized methods
+ * |-----------------------|
* | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
* |-----------------------|
* ~ alignment padding ~ // To make the whole frame 16 byte aligned.
@@ -1235,6 +1252,8 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* | incoming arguments |
* +=======================+ <---- Caller's SP
+ * | OSR padding | // If required
+ * |-----------------------|
* | Varargs regs space | // Only for varargs main functions; 64 bytes
* |-----------------------|
* | Saved LR | // 8 bytes
@@ -1243,14 +1262,16 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* |-----------------------|
* |Callee saved registers | // multiple of 8 bytes
* |-----------------------|
+ * | MonitorAcquired | // 8 bytes; for synchronized methods
+ * |-----------------------|
* | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
* |-----------------------|
- * ~ alignment padding ~ // To make the first SP subtraction 16 byte aligned
+ * ~ alignment padding ~ // To make the first SP subtraction 16 byte aligned <-- SP after first adjustment (points at alignment padding or PSP slot)
* |-----------------------|
* ~ alignment padding ~ // To make the whole frame 16 byte aligned (specifically, to 16-byte align the outgoing argument space).
* |-----------------------|
* | Outgoing arg space | // multiple of 8 bytes
- * |-----------------------| <---- Ambient SP
+ * |-----------------------| <---- Ambient SP (SP after second adjustment)
* | | |
* ~ | Stack grows ~
* | | downward |
@@ -1310,6 +1331,8 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in
* ldp fp,lr,[sp],#framesz
* ret lr
*
+ * See CodeGen::genPushCalleeSavedRegisters() for a description of the main function frame layout.
+ * See Compiler::lvaAssignVirtualFrameOffsetsToLocals() for calculation of main frame local variable offsets.
*/
// clang-format on
@@ -1354,10 +1377,12 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
if (genFuncletInfo.fiFrameType == 1)
{
- // With OSR we may see large values for fiSpDelta1
- // (we really need to probe the frame, sigh)
if (compiler->opts.IsOSR())
{
+ // With OSR we may see large values for fiSpDelta1.
+ // We repurpose genAllocLclFram to do the necessary probing.
+ bool scratchRegIsZero = false;
+ genAllocLclFrame(-genFuncletInfo.fiSpDelta1, REG_SCRATCH, &scratchRegIsZero, maskArgRegsLiveIn);
genStackPointerAdjustment(genFuncletInfo.fiSpDelta1, REG_SCRATCH, nullptr, /* reportUnwindData */ true);
GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, 0);
compiler->unwindSaveRegPair(REG_FP, REG_LR, 0);
@@ -1393,10 +1418,12 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
}
else if (genFuncletInfo.fiFrameType == 3)
{
- // With OSR we may see large values for fiSpDelta1
- // (we really need to probe the frame, sigh)
if (compiler->opts.IsOSR())
{
+ // With OSR we may see large values for fiSpDelta1
+ // We repurpose genAllocLclFram to do the necessary probing.
+ bool scratchRegIsZero = false;
+ genAllocLclFrame(-genFuncletInfo.fiSpDelta1, REG_SCRATCH, &scratchRegIsZero, maskArgRegsLiveIn);
genStackPointerAdjustment(genFuncletInfo.fiSpDelta1, REG_SCRATCH, nullptr, /* reportUnwindData */ true);
GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, 0);
compiler->unwindSaveRegPair(REG_FP, REG_LR, 0);
@@ -1427,11 +1454,17 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
if (compiler->opts.IsOSR())
{
+ // With OSR we may see large values for fiSpDelta1.
+ // We repurpose genAllocLclFram to do the necessary probing.
+ bool scratchRegIsZero = false;
+ genAllocLclFrame(-genFuncletInfo.fiSpDelta1, REG_SCRATCH, &scratchRegIsZero, maskArgRegsLiveIn);
genStackPointerAdjustment(genFuncletInfo.fiSpDelta1, REG_SCRATCH, nullptr, /* reportUnwindData */ true);
}
else
{
- // Nothing to do here; the first SP adjustment will be done by saving the callee-saved registers.
+ assert(genFuncletInfo.fiSpDelta1 < 0);
+ assert(genFuncletInfo.fiSpDelta1 >= -240);
+ genStackPointerAdjustment(genFuncletInfo.fiSpDelta1, REG_NA, nullptr, /* reportUnwindData */ true);
}
}
@@ -1501,6 +1534,8 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
/*****************************************************************************
*
* Generates code for an EH funclet epilog.
+ *
+ * See the description of frame shapes at genFuncletProlog().
*/
void CodeGen::genFuncletEpilog()
@@ -1675,6 +1710,9 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
if (compiler->opts.IsOSR() && (PSPSize > 0))
{
osrPad = compiler->info.compPatchpointInfo->TotalFrameSize();
+
+ // OSR pad must be already aligned to stack size.
+ assert((osrPad % STACK_ALIGN) == 0);
}
genFuncletInfo.fiFunction_CallerSP_to_FP_delta = genCallerSPtoFPdelta() - osrPad;
@@ -1705,31 +1743,41 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
assert(compiler->lvaOutgoingArgSpaceSize % REGSIZE_BYTES == 0);
unsigned const outgoingArgSpaceAligned = roundUp(compiler->lvaOutgoingArgSpaceSize, STACK_ALIGN);
- unsigned const maxFuncletFrameSizeAligned = saveRegsPlusPSPSizeAligned + osrPad + outgoingArgSpaceAligned;
- assert((maxFuncletFrameSizeAligned % STACK_ALIGN) == 0);
+ // If do two SP adjustments, each one must be aligned. This represents the largest possible stack size, if two
+ // separate alignment slots are required.
+ unsigned const twoSpAdjustmentFuncletFrameSizeAligned =
+ osrPad + saveRegsPlusPSPSizeAligned + outgoingArgSpaceAligned;
+ assert((twoSpAdjustmentFuncletFrameSizeAligned % STACK_ALIGN) == 0);
int SP_to_FPLR_save_delta;
int SP_to_PSP_slot_delta;
int CallerSP_to_PSP_slot_delta;
- unsigned const funcletFrameSize = saveRegsPlusPSPSize + osrPad + compiler->lvaOutgoingArgSpaceSize;
- unsigned const funcletFrameSizeAligned = roundUp(funcletFrameSize, STACK_ALIGN);
- assert(funcletFrameSizeAligned <= maxFuncletFrameSizeAligned);
-
- unsigned const funcletFrameAlignmentPad = funcletFrameSizeAligned - funcletFrameSize;
- assert((funcletFrameAlignmentPad == 0) || (funcletFrameAlignmentPad == REGSIZE_BYTES));
+ // Are we stressing frame type 5? Don't do it unless we have non-zero outgoing arg space.
+ const bool useFrameType5 =
+ genSaveFpLrWithAllCalleeSavedRegisters && genForceFuncletFrameType5 && (compiler->lvaOutgoingArgSpaceSize > 0);
- if (maxFuncletFrameSizeAligned <= 512)
+ if ((twoSpAdjustmentFuncletFrameSizeAligned <= 512) && !useFrameType5)
{
+ unsigned const oneSpAdjustmentFuncletFrameSize =
+ osrPad + saveRegsPlusPSPSize + compiler->lvaOutgoingArgSpaceSize;
+ unsigned const oneSpAdjustmentFuncletFrameSizeAligned = roundUp(oneSpAdjustmentFuncletFrameSize, STACK_ALIGN);
+ assert(oneSpAdjustmentFuncletFrameSizeAligned <= twoSpAdjustmentFuncletFrameSizeAligned);
+
+ unsigned const oneSpAdjustmentFuncletFrameSizeAlignmentPad =
+ oneSpAdjustmentFuncletFrameSizeAligned - oneSpAdjustmentFuncletFrameSize;
+ assert((oneSpAdjustmentFuncletFrameSizeAlignmentPad == 0) ||
+ (oneSpAdjustmentFuncletFrameSizeAlignmentPad == REGSIZE_BYTES));
+
if (genSaveFpLrWithAllCalleeSavedRegisters)
{
- SP_to_FPLR_save_delta = funcletFrameSizeAligned - (2 /* FP, LR */ * REGSIZE_BYTES);
+ SP_to_FPLR_save_delta = oneSpAdjustmentFuncletFrameSizeAligned - (2 /* FP, LR */ * REGSIZE_BYTES);
if (compiler->info.compIsVarArgs)
{
SP_to_FPLR_save_delta -= MAX_REG_ARG * REGSIZE_BYTES;
}
- SP_to_PSP_slot_delta = compiler->lvaOutgoingArgSpaceSize + funcletFrameAlignmentPad + osrPad;
+ SP_to_PSP_slot_delta = compiler->lvaOutgoingArgSpaceSize + oneSpAdjustmentFuncletFrameSizeAlignmentPad;
CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize);
genFuncletInfo.fiFrameType = 4;
@@ -1737,7 +1785,8 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
else
{
SP_to_FPLR_save_delta = compiler->lvaOutgoingArgSpaceSize;
- SP_to_PSP_slot_delta = SP_to_FPLR_save_delta + 2 /* FP, LR */ * REGSIZE_BYTES + funcletFrameAlignmentPad;
+ SP_to_PSP_slot_delta =
+ SP_to_FPLR_save_delta + 2 /* FP, LR */ * REGSIZE_BYTES + oneSpAdjustmentFuncletFrameSizeAlignmentPad;
CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize - 2 /* FP, LR */ * REGSIZE_BYTES);
if (compiler->lvaOutgoingArgSpaceSize == 0)
@@ -1750,26 +1799,25 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
}
}
- genFuncletInfo.fiSpDelta1 = -(int)funcletFrameSizeAligned;
+ genFuncletInfo.fiSpDelta1 = -(int)oneSpAdjustmentFuncletFrameSizeAligned;
genFuncletInfo.fiSpDelta2 = 0;
- assert(genFuncletInfo.fiSpDelta1 + genFuncletInfo.fiSpDelta2 == -(int)funcletFrameSizeAligned);
+ assert(genFuncletInfo.fiSpDelta1 + genFuncletInfo.fiSpDelta2 == -(int)oneSpAdjustmentFuncletFrameSizeAligned);
}
else
{
- unsigned saveRegsPlusPSPAlignmentPad = saveRegsPlusPSPSizeAligned - saveRegsPlusPSPSize;
+ unsigned const saveRegsPlusPSPAlignmentPad = saveRegsPlusPSPSizeAligned - saveRegsPlusPSPSize;
assert((saveRegsPlusPSPAlignmentPad == 0) || (saveRegsPlusPSPAlignmentPad == REGSIZE_BYTES));
if (genSaveFpLrWithAllCalleeSavedRegisters)
{
- SP_to_FPLR_save_delta = funcletFrameSizeAligned - (2 /* FP, LR */ * REGSIZE_BYTES);
+ SP_to_FPLR_save_delta = twoSpAdjustmentFuncletFrameSizeAligned - (2 /* FP, LR */ * REGSIZE_BYTES);
if (compiler->info.compIsVarArgs)
{
SP_to_FPLR_save_delta -= MAX_REG_ARG * REGSIZE_BYTES;
}
- SP_to_PSP_slot_delta =
- compiler->lvaOutgoingArgSpaceSize + funcletFrameAlignmentPad + saveRegsPlusPSPAlignmentPad;
+ SP_to_PSP_slot_delta = outgoingArgSpaceAligned + saveRegsPlusPSPAlignmentPad;
CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize);
genFuncletInfo.fiFrameType = 5;
@@ -1787,7 +1835,7 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
genFuncletInfo.fiSpDelta1 = -(int)(osrPad + saveRegsPlusPSPSizeAligned);
genFuncletInfo.fiSpDelta2 = -(int)outgoingArgSpaceAligned;
- assert(genFuncletInfo.fiSpDelta1 + genFuncletInfo.fiSpDelta2 == -(int)maxFuncletFrameSizeAligned);
+ assert(genFuncletInfo.fiSpDelta1 + genFuncletInfo.fiSpDelta2 == -(int)twoSpAdjustmentFuncletFrameSizeAligned);
}
/* Now save it for future use */
@@ -2178,7 +2226,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
{
if (emitter::emitIns_valid_imm_for_mov(imm, size))
{
- GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm);
+ GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, INS_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags));
}
else
{
@@ -2224,7 +2272,9 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
imm16 = ~imm16;
}
- GetEmitter()->emitIns_R_I_I(ins, size, reg, imm16, i, INS_OPTS_LSL);
+ GetEmitter()->emitIns_R_I_I(ins, size, reg, imm16, i,
+ INS_OPTS_LSL DEBUGARG(i == 0 ? targetHandle : 0)
+ DEBUGARG(i == 0 ? gtFlags : GTF_EMPTY));
// Once the initial movz/movn is emitted the remaining instructions will all use movk
ins = INS_movk;
@@ -2258,8 +2308,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
{
case GT_CNS_INT:
{
- GenTreeIntConCommon* con = tree->AsIntConCommon();
- ssize_t cnsVal = con->IconValue();
+ GenTreeIntCon* con = tree->AsIntCon();
+ ssize_t cnsVal = con->IconValue();
emitAttr attr = emitActualTypeSize(targetType);
// TODO-CQ: Currently we cannot do this for all handles because of
@@ -2275,8 +2325,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
}
instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal,
- INS_FLAGS_DONT_CARE DEBUGARG(tree->AsIntCon()->gtTargetHandle)
- DEBUGARG(tree->AsIntCon()->gtFlags));
+ INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags));
regSet.verifyRegUsed(targetReg);
}
break;
@@ -4527,6 +4576,19 @@ void CodeGen::SetSaveFpLrWithAllCalleeSavedRegisters(bool value)
{
JITDUMP("Setting genSaveFpLrWithAllCalleeSavedRegisters to %s\n", dspBool(value));
genSaveFpLrWithAllCalleeSavedRegisters = value;
+
+ if (genSaveFpLrWithAllCalleeSavedRegisters)
+ {
+ // We'll use frame type 4 or 5. Frame type 5 only occurs if there is a very large outgoing argument
+ // space. This is extremely rare, so under stress force using this frame type. However, frame type 5
+ // isn't used if there is no outgoing argument space; this is checked elsewhere.
+
+ if ((compiler->opts.compJitSaveFpLrWithCalleeSavedRegisters == 3) ||
+ compiler->compStressCompile(Compiler::STRESS_GENERIC_VARN, 50))
+ {
+ genForceFuncletFrameType5 = true;
+ }
+ }
}
//---------------------------------------------------------------------
@@ -10281,18 +10343,34 @@ void CodeGen::genCodeForAddEx(GenTreeOp* tree)
//
void CodeGen::genCodeForCond(GenTreeOp* tree)
{
- assert(tree->OperIs(GT_CSNEG_MI));
- assert(!(tree->gtFlags & GTF_SET_FLAGS) && (tree->gtFlags & GTF_USE_FLAGS));
+ assert(tree->OperIs(GT_CSNEG_MI, GT_CNEG_LT));
+ assert(!(tree->gtFlags & GTF_SET_FLAGS));
genConsumeOperands(tree);
- instruction ins;
- insCond cond;
switch (tree->OperGet())
{
case GT_CSNEG_MI:
{
- ins = INS_csneg;
- cond = INS_COND_MI;
+ instruction ins = INS_csneg;
+ insCond cond = INS_COND_MI;
+
+ regNumber dstReg = tree->GetRegNum();
+ regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
+ regNumber op2Reg = tree->gtGetOp2()->GetRegNum();
+
+ GetEmitter()->emitIns_R_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, op2Reg, cond);
+ break;
+ }
+
+ case GT_CNEG_LT:
+ {
+ instruction ins = INS_cneg;
+ insCond cond = INS_COND_LT;
+
+ regNumber dstReg = tree->GetRegNum();
+ regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
+
+ GetEmitter()->emitIns_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, cond);
break;
}
@@ -10300,11 +10378,6 @@ void CodeGen::genCodeForCond(GenTreeOp* tree)
unreached();
}
- regNumber dstReg = tree->GetRegNum();
- regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
- regNumber op2Reg = tree->gtGetOp2()->GetRegNum();
-
- GetEmitter()->emitIns_R_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, op2Reg, cond);
genProduceReg(tree);
}
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index f5de40823259e4..dd4c25c5f07636 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -324,6 +324,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
break;
case GT_CSNEG_MI:
+ case GT_CNEG_LT:
genCodeForCond(treeNode->AsOp());
break;
#endif // TARGET_ARM64
@@ -770,9 +771,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
GenTree* source = treeNode->gtGetOp1();
- bool isStruct = source->TypeIs(TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
-
- if (!isStruct) // a normal non-Struct argument
+ if (!source->TypeIs(TYP_STRUCT)) // a normal non-Struct argument
{
if (varTypeIsSIMD(source->TypeGet()))
{
@@ -864,9 +863,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
{
genPutArgStkFieldList(treeNode, varNumOut);
}
- else // We must have a GT_OBJ or a GT_LCL_VAR
+ else
{
- noway_assert(source->OperIs(GT_LCL_VAR, GT_OBJ));
+ noway_assert(source->OperIsLocalRead() || source->OperIs(GT_OBJ));
var_types targetType = source->TypeGet();
noway_assert(varTypeIsStruct(targetType));
@@ -879,96 +878,42 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
#ifdef TARGET_ARM64
regNumber hiReg = treeNode->GetSingleTempReg();
#endif // TARGET_ARM64
- regNumber addrReg = REG_NA;
- GenTreeLclVarCommon* varNode = nullptr;
- GenTree* addrNode = nullptr;
+ GenTreeLclVarCommon* srcLclNode = nullptr;
+ regNumber addrReg = REG_NA;
+ ClassLayout* layout = nullptr;
- if (source->OperGet() == GT_LCL_VAR)
+ // Setup "layout", "srcLclNode" and "addrReg".
+ if (source->OperIsLocalRead())
{
- varNode = source->AsLclVarCommon();
+ srcLclNode = source->AsLclVarCommon();
+ layout = srcLclNode->GetLayout(compiler);
+ LclVarDsc* varDsc = compiler->lvaGetDesc(srcLclNode);
+
+ // This struct must live on the stack frame.
+ assert(varDsc->lvOnFrame && !varDsc->lvRegister);
}
else // we must have a GT_OBJ
{
- assert(source->OperGet() == GT_OBJ);
-
- addrNode = source->AsOp()->gtOp1;
+ layout = source->AsObj()->GetLayout();
+ addrReg = genConsumeReg(source->AsObj()->Addr());
- // addrNode can either be a GT_LCL_VAR_ADDR or an address expression
- //
- if (addrNode->OperGet() == GT_LCL_VAR_ADDR)
+#ifdef TARGET_ARM64
+ // If addrReg equal to loReg, swap(loReg, hiReg)
+ // This reduces code complexity by only supporting one addrReg overwrite case
+ if (loReg == addrReg)
{
- // We have a GT_OBJ(GT_LCL_VAR_ADDR)
- //
- // We will treat this case the same as above
- // (i.e if we just had this GT_LCL_VAR directly as the source)
- // so update 'source' to point this GT_LCL_VAR_ADDR node
- // and continue to the codegen for the LCL_VAR node below
- //
- assert(addrNode->isContained());
- varNode = addrNode->AsLclVarCommon();
- addrNode = nullptr;
+ loReg = hiReg;
+ hiReg = addrReg;
}
- else // addrNode is used
- {
- // TODO-Cleanup: `Lowering::NewPutArg` marks only `LCL_VAR_ADDR` as contained nowadays,
- // but we use `genConsumeAddress` as a precaution, use `genConsumeReg()` instead.
- assert(!addrNode->isContained());
- // Generate code to load the address that we need into a register
- genConsumeAddress(addrNode);
- addrReg = addrNode->GetRegNum();
-
-#ifdef TARGET_ARM64
- // If addrReg equal to loReg, swap(loReg, hiReg)
- // This reduces code complexity by only supporting one addrReg overwrite case
- if (loReg == addrReg)
- {
- loReg = hiReg;
- hiReg = addrReg;
- }
#endif // TARGET_ARM64
- }
}
- // Either varNode or addrNOde must have been setup above,
- // the xor ensures that only one of the two is setup, not both
- assert((varNode != nullptr) ^ (addrNode != nullptr));
-
- ClassLayout* layout;
- unsigned srcSize;
- bool isHfa;
-
- // Setup the srcSize, isHFa, and gcPtrCount
- if (source->OperGet() == GT_LCL_VAR)
- {
- assert(varNode != nullptr);
- LclVarDsc* varDsc = compiler->lvaGetDesc(varNode);
-
- // This struct also must live in the stack frame
- // And it can't live in a register (SIMD)
- assert(varDsc->lvType == TYP_STRUCT);
- assert(varDsc->lvOnFrame && !varDsc->lvRegister);
-
- srcSize = varDsc->lvSize();
- isHfa = varDsc->lvIsHfa();
- layout = varDsc->GetLayout();
- }
- else // we must have a GT_OBJ
- {
- assert(source->OperGet() == GT_OBJ);
-
- // If the source is an OBJ node then we need to use the type information
- // it provides (size and GC layout) even if the node wraps a lclvar. Due
- // to struct reinterpretation (e.g. Unsafe.As) it is possible that
- // the OBJ node has a different type than the lclvar.
- layout = source->AsObj()->GetLayout();
- srcSize = layout->GetSize();
- isHfa = compiler->IsHfa(layout->GetClassHandle());
- }
+ unsigned srcSize = layout->GetSize();
// If we have an HFA we can't have any GC pointers,
// if not then the max size for the struct is 16 bytes
- if (isHfa)
+ if (compiler->IsHfa(layout->GetClassHandle()))
{
noway_assert(!layout->HasGCPtr());
}
@@ -981,45 +926,32 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
noway_assert(srcSize <= MAX_PASS_MULTIREG_BYTES);
#endif // TARGET_ARM64
- unsigned structSize;
-
unsigned dstSize = treeNode->GetStackByteSize();
- if (dstSize != srcSize)
+
+ // We can generate smaller code if store size is a multiple of TARGET_POINTER_SIZE.
+ // The dst size can be rounded up to PUTARG_STK size. The src size can be rounded up
+ // if it reads a local variable because reading "too much" from a local cannot fault.
+ // We must also be careful to check for the arm64 apple case where arguments can be
+ // passed without padding.
+ //
+ if ((dstSize != srcSize) && (srcLclNode != nullptr))
{
- // We can generate a smaller code if store size is a multiple of TARGET_POINTER_SIZE.
- // The dst size can be rounded up to PUTARG_STK size.
- // The src size can be rounded up if it reads a local variable slot because the local
- // variable stack allocation size is rounded up to be a multiple of the TARGET_POINTER_SIZE.
- // The exception is arm64 apple arguments because they can be passed without padding.
- if (varNode != nullptr)
+ unsigned widenedSrcSize = roundUp(srcSize, TARGET_POINTER_SIZE);
+ if (widenedSrcSize <= dstSize)
{
- // If we have a varNode, even if it was casted using `OBJ`, we can read its original memory size.
- const LclVarDsc* varDsc = compiler->lvaGetDesc(varNode);
- const unsigned varStackSize = varDsc->lvSize();
- if (varStackSize >= srcSize)
- {
- srcSize = varStackSize;
- }
+ srcSize = widenedSrcSize;
}
}
- if (dstSize == srcSize)
- {
- structSize = dstSize;
- }
- else
- {
- // With Unsafe object cast we can have different strange combinations:
- // PutArgStk<8>(Obj<16>(LclVar<8>)) -> copy 8 bytes;
- // PutArgStk<16>(Obj<16>(LclVar<8>)) -> copy 16 bytes, reading undefined memory after the local.
- structSize = min(dstSize, srcSize);
- }
- int remainingSize = structSize;
+ assert(srcSize <= dstSize);
+
+ int remainingSize = srcSize;
unsigned structOffset = 0;
+ unsigned lclOffset = (srcLclNode != nullptr) ? srcLclNode->GetLclOffs() : 0;
unsigned nextIndex = 0;
#ifdef TARGET_ARM64
- // For a >= 16-byte structSize we will generate a ldp and stp instruction each loop
+ // For a >= 16-byte sizes we will generate a ldp and stp instruction each loop
// ldp x2, x3, [x0]
// stp x2, x3, [sp, #16]
@@ -1028,11 +960,11 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
var_types type0 = layout->GetGCPtrType(nextIndex + 0);
var_types type1 = layout->GetGCPtrType(nextIndex + 1);
- if (varNode != nullptr)
+ if (srcLclNode != nullptr)
{
- // Load from our varNumImp source
+ // Load from our local source
emit->emitIns_R_R_S_S(INS_ldp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg,
- varNode->GetLclNum(), structOffset);
+ srcLclNode->GetLclNum(), lclOffset + structOffset);
}
else
{
@@ -1056,17 +988,18 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
nextIndex += 2;
}
#else // TARGET_ARM
- // For a >= 4 byte structSize we will generate a ldr and str instruction each loop
+ // For a >= 4 byte sizes we will generate a ldr and str instruction each loop
// ldr r2, [r0]
// str r2, [sp, #16]
while (remainingSize >= TARGET_POINTER_SIZE)
{
var_types type = layout->GetGCPtrType(nextIndex);
- if (varNode != nullptr)
+ if (srcLclNode != nullptr)
{
- // Load from our varNumImp source
- emit->emitIns_R_S(INS_ldr, emitTypeSize(type), loReg, varNode->GetLclNum(), structOffset);
+ // Load from our local source
+ emit->emitIns_R_S(INS_ldr, emitTypeSize(type), loReg, srcLclNode->GetLclNum(),
+ lclOffset + structOffset);
}
else
{
@@ -1088,7 +1021,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
}
#endif // TARGET_ARM
- // For a 12-byte structSize we will generate two load instructions
+ // For a 12-byte size we will generate two load instructions
// ldr x2, [x0]
// ldr w3, [x0, #8]
// str x2, [sp, #16]
@@ -1129,10 +1062,10 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
remainingSize -= moveSize;
instruction loadIns = ins_Load(type);
- if (varNode != nullptr)
+ if (srcLclNode != nullptr)
{
- // Load from our varNumImp source
- emit->emitIns_R_S(loadIns, attr, loReg, varNode->GetLclNum(), structOffset);
+ // Load from our local source
+ emit->emitIns_R_S(loadIns, attr, loReg, srcLclNode->GetLclNum(), lclOffset + structOffset);
}
else
{
@@ -1306,14 +1239,8 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
}
else // addrNode is used
{
- assert(addrNode != nullptr);
- // TODO-Cleanup: `Lowering::NewPutArg` marks only `LCL_VAR_ADDR` as contained nowadays,
- // but we use `genConsumeAddress` as a precaution, use `genConsumeReg()` instead.
- assert(!addrNode->isContained());
-
// Generate code to load the address that we need into a register
- genConsumeAddress(addrNode);
- addrReg = addrNode->GetRegNum();
+ addrReg = genConsumeReg(addrNode);
// If addrReg equal to baseReg, we use the last target register as alternative baseReg.
// Because the candidate mask for the internal baseReg does not include any of the target register,
@@ -1327,21 +1254,40 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
ClassLayout* layout = source->AsObj()->GetLayout();
// Put on stack first
- unsigned nextIndex = treeNode->gtNumRegs;
- unsigned structOffset = nextIndex * TARGET_POINTER_SIZE;
- int remainingSize = treeNode->GetStackByteSize();
+ unsigned structOffset = treeNode->gtNumRegs * TARGET_POINTER_SIZE;
+ unsigned remainingSize = layout->GetSize() - structOffset;
unsigned argOffsetOut = treeNode->getArgOffset();
- // remainingSize is always multiple of TARGET_POINTER_SIZE
- assert(remainingSize % TARGET_POINTER_SIZE == 0);
+ assert((remainingSize > 0) && (roundUp(remainingSize, TARGET_POINTER_SIZE) == treeNode->GetStackByteSize()));
while (remainingSize > 0)
{
- var_types type = layout->GetGCPtrType(nextIndex);
+ var_types type;
+ if (remainingSize >= TARGET_POINTER_SIZE)
+ {
+ type = layout->GetGCPtrType(structOffset / TARGET_POINTER_SIZE);
+ }
+ else if (remainingSize >= 4)
+ {
+ type = TYP_INT;
+ }
+ else if (remainingSize >= 2)
+ {
+ type = TYP_USHORT;
+ }
+ else
+ {
+ assert(remainingSize == 1);
+ type = TYP_UBYTE;
+ }
+
+ emitAttr attr = emitActualTypeSize(type);
+ unsigned moveSize = genTypeSize(type);
+ instruction loadIns = ins_Load(type);
if (varNode != nullptr)
{
- // Load from our varNumImp source
- emit->emitIns_R_S(INS_ldr, emitTypeSize(type), baseReg, srcVarNum, structOffset);
+ // Load from our local source
+ emit->emitIns_R_S(loadIns, attr, baseReg, srcVarNum, structOffset);
}
else
{
@@ -1349,16 +1295,16 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
assert(baseReg != addrReg);
// Load from our address expression source
- emit->emitIns_R_R_I(INS_ldr, emitTypeSize(type), baseReg, addrReg, structOffset);
+ emit->emitIns_R_R_I(loadIns, attr, baseReg, addrReg, structOffset);
}
- // Emit str instruction to store the register into the outgoing argument area
- emit->emitIns_S_R(INS_str, emitTypeSize(type), baseReg, varNumOut, argOffsetOut);
- argOffsetOut += TARGET_POINTER_SIZE; // We stored 4-bytes of the struct
- assert(argOffsetOut <= argOffsetMax); // We can't write beyond the outgoing arg area
- remainingSize -= TARGET_POINTER_SIZE; // We loaded 4-bytes of the struct
- structOffset += TARGET_POINTER_SIZE;
- nextIndex += 1;
+ // Emit the instruction to store the register into the outgoing argument area
+ emit->emitIns_S_R(ins_Store(type), attr, baseReg, varNumOut, argOffsetOut);
+ argOffsetOut += moveSize;
+ assert(argOffsetOut <= argOffsetMax);
+
+ remainingSize -= moveSize;
+ structOffset += moveSize;
}
// We set up the registers in order, so that we assign the last target register `baseReg` is no longer in use,
@@ -1371,7 +1317,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
if (varNode != nullptr)
{
- // Load from our varNumImp source
+ // Load from our local source
emit->emitIns_R_S(INS_ldr, emitTypeSize(type), targetReg, srcVarNum, structOffset);
}
else
@@ -3929,14 +3875,9 @@ void CodeGen::genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& d
case GenIntCastDesc::CHECK_INT_RANGE:
{
- const regNumber tempReg = cast->GetSingleTempReg();
- assert(tempReg != reg);
- instGen_Set_Reg_To_Imm(EA_8BYTE, tempReg, INT32_MAX);
- GetEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, tempReg);
- genJumpToThrowHlpBlk(EJ_gt, SCK_OVERFLOW);
- instGen_Set_Reg_To_Imm(EA_8BYTE, tempReg, INT32_MIN);
- GetEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, tempReg);
- genJumpToThrowHlpBlk(EJ_lt, SCK_OVERFLOW);
+ // Emit "if ((long)(int)x != x) goto OVERFLOW"
+ GetEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, reg, INS_OPTS_SXTW);
+ genJumpToThrowHlpBlk(EJ_ne, SCK_OVERFLOW);
}
break;
#endif
@@ -4485,6 +4426,16 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
}
else
{
+#ifdef TARGET_ARM64
+ // Handle LEA with "contained" BFIZ
+ if (index->isContained() && index->OperIs(GT_BFIZ))
+ {
+ assert(scale == 0);
+ scale = (DWORD)index->gtGetOp2()->AsIntConCommon()->IconValue();
+ index = index->gtGetOp1()->gtGetOp1();
+ }
+#endif
+
// Then compute target reg from [base + index*scale]
genScaledAdd(size, lea->GetRegNum(), memBase->GetRegNum(), index->GetRegNum(), scale);
}
@@ -4706,7 +4657,7 @@ void CodeGen::genPushCalleeSavedRegisters()
// |-----------------------|
// |Callee saved registers | // not including FP/LR; multiple of 8 bytes
// |-----------------------|
- // | MonitorAcquired |
+ // | MonitorAcquired | // 8 bytes; for synchronized methods
// |-----------------------|
// | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
// |-----------------------|
@@ -4739,7 +4690,7 @@ void CodeGen::genPushCalleeSavedRegisters()
// |-----------------------|
// |Callee saved registers | // not including FP/LR; multiple of 8 bytes
// |-----------------------|
- // | MonitorAcquired |
+ // | MonitorAcquired | // 8 bytes; for synchronized methods
// |-----------------------|
// | PSP slot | // 8 bytes (omitted in NativeAOT ABI)
// |-----------------------|
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index ed16b9a9fa4984..d158a562116987 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -137,6 +137,7 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
#ifdef TARGET_ARM64
genSaveFpLrWithAllCalleeSavedRegisters = false;
+ genForceFuncletFrameType5 = false;
#endif // TARGET_ARM64
}
@@ -6581,35 +6582,6 @@ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass)
return varTypeIsValidHfaType(GetHfaType(hClass));
}
-bool Compiler::IsHfa(GenTree* tree)
-{
- if (GlobalJitOptions::compFeatureHfa)
- {
- return IsHfa(gtGetStructHandleIfPresent(tree));
- }
- else
- {
- return false;
- }
-}
-
-var_types Compiler::GetHfaType(GenTree* tree)
-{
- if (GlobalJitOptions::compFeatureHfa)
- {
- return GetHfaType(gtGetStructHandleIfPresent(tree));
- }
- else
- {
- return TYP_UNDEF;
- }
-}
-
-unsigned Compiler::GetHfaCount(GenTree* tree)
-{
- return GetHfaCount(gtGetStructHandle(tree));
-}
-
var_types Compiler::GetHfaType(CORINFO_CLASS_HANDLE hClass)
{
if (GlobalJitOptions::compFeatureHfa)
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index 6a44f1db324de1..b9026bfedd6f40 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -753,24 +753,27 @@ void CodeGen::genCodeForBBlist()
break;
case BBJ_ALWAYS:
- inst_JMP(EJ_jmp, block->bbJumpDest
+#ifdef TARGET_XARCH
+ {
+ // If a block was selected to place an alignment instruction because it ended
+ // with a jump, do not remove jumps from such blocks.
+ // Do not remove a jump between hot and cold regions.
+ bool isRemovableJmpCandidate =
+ !block->hasAlign() && !compiler->fgInDifferentRegions(block, block->bbJumpDest);
+
#ifdef TARGET_AMD64
- // AMD64 requires an instruction after a call instruction for unwinding
- // inside an EH region so if the last instruction generated was a call instruction
- // do not allow this jump to be marked for possible later removal.
- //
- // If a block was selected to place an alignment instruction because it ended
- // with a jump, do not remove jumps from such blocks.
- ,
- /* isRemovableJmpCandidate */ !GetEmitter()->emitIsLastInsCall() && !block->hasAlign()
+ // AMD64 requires an instruction after a call instruction for unwinding
+ // inside an EH region so if the last instruction generated was a call instruction
+ // do not allow this jump to be marked for possible later removal.
+ isRemovableJmpCandidate = isRemovableJmpCandidate && !GetEmitter()->emitIsLastInsCall();
+#endif // TARGET_AMD64
+
+ inst_JMP(EJ_jmp, block->bbJumpDest, isRemovableJmpCandidate);
+ }
#else
-#ifdef TARGET_XARCH
- ,
- /* isRemovableJmpCandidate */ !block->hasAlign()
-#endif
+ inst_JMP(EJ_jmp, block->bbJumpDest);
+#endif // TARGET_XARCH
-#endif
- );
FALLTHROUGH;
case BBJ_COND:
@@ -1762,21 +1765,21 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode,
regNumber sizeReg)
{
// The putArgNode children are always contained. We should not consume any registers.
- assert(putArgNode->gtGetOp1()->isContained());
+ assert(putArgNode->Data()->isContained());
- // Get the source address.
- GenTree* src = putArgNode->gtGetOp1();
+ // Get the source.
+ GenTree* src = putArgNode->Data();
+ regNumber srcAddrReg = REG_NA;
assert(varTypeIsStruct(src));
- assert((src->gtOper == GT_OBJ) || ((src->gtOper == GT_IND && varTypeIsSIMD(src))));
- GenTree* srcAddr = src->gtGetOp1();
+ assert(src->OperIs(GT_OBJ) || src->OperIsLocalRead() || (src->OperIs(GT_IND) && varTypeIsSIMD(src)));
assert(dstReg != REG_NA);
assert(srcReg != REG_NA);
- // Consume the registers only if they are not contained or set to REG_NA.
- if (srcAddr->GetRegNum() != REG_NA)
+ // Consume the register for the source address if needed.
+ if (src->OperIsIndir())
{
- genConsumeReg(srcAddr);
+ srcAddrReg = genConsumeReg(src->AsIndir()->Addr());
}
// If the op1 is already in the dstReg - nothing to do.
@@ -1798,22 +1801,17 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode,
}
#endif // !TARGET_X86
- if (srcAddr->OperIsLocalAddr())
+ if (srcAddrReg != REG_NA)
{
- // The OperLocalAddr is always contained.
- assert(srcAddr->isContained());
- const GenTreeLclVarCommon* lclNode = srcAddr->AsLclVarCommon();
-
- // Generate LEA instruction to load the LclVar address in RSI.
- // Source is known to be on the stack. Use EA_PTRSIZE.
- unsigned int offset = lclNode->GetLclOffs();
- GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, lclNode->GetLclNum(), offset);
+ // Source is not known to be on the stack. Use EA_BYREF.
+ GetEmitter()->emitIns_Mov(INS_mov, EA_BYREF, srcReg, srcAddrReg, /* canSkip */ true);
}
else
{
- assert(srcAddr->GetRegNum() != REG_NA);
- // Source is not known to be on the stack. Use EA_BYREF.
- GetEmitter()->emitIns_Mov(INS_mov, EA_BYREF, srcReg, srcAddr->GetRegNum(), /* canSkip */ true);
+ // Generate LEA instruction to load the LclVar address in RSI.
+ // Source is known to be on the stack. Use EA_PTRSIZE.
+ GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, src->AsLclVarCommon()->GetLclNum(),
+ src->AsLclVarCommon()->GetLclOffs());
}
if (sizeReg != REG_NA)
diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp
index 67d64db8c6a821..7f85403db40171 100644
--- a/src/coreclr/jit/codegenloongarch64.cpp
+++ b/src/coreclr/jit/codegenloongarch64.cpp
@@ -1655,6 +1655,18 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/
+//------------------------------------------------------------------------
+// inst_JMP: Generate a jump instruction.
+//
+void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock)
+{
+#if !FEATURE_FIXED_OUT_ARGS
+ assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed());
+#endif // !FEATURE_FIXED_OUT_ARGS
+
+ GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock);
+}
+
BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
{
// Generate a call to the finally, like this:
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 039d4c11185225..ca050dcf49ff90 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -442,7 +442,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
}
else
{
- GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(gtFlags));
+ GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags));
}
}
regSet.verifyRegUsed(reg);
@@ -462,8 +462,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
{
// relocatable values tend to come down as a CNS_INT of native int type
// so the line between these two opcodes is kind of blurry
- GenTreeIntConCommon* con = tree->AsIntConCommon();
- ssize_t cnsVal = con->IconValue();
+ GenTreeIntCon* con = tree->AsIntCon();
+ ssize_t cnsVal = con->IconValue();
emitAttr attr = emitActualTypeSize(targetType);
// Currently this cannot be done for all handles due to
@@ -482,7 +482,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
attr = EA_SET_FLG(attr, EA_BYREF_FLG);
}
- instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, INS_FLAGS_DONT_CARE DEBUGARG(0) DEBUGARG(tree->gtFlags));
+ instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal,
+ INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags));
regSet.verifyRegUsed(targetReg);
}
break;
@@ -516,22 +517,28 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
if (vecCon->IsAllBitsSet())
{
+ if ((attr != EA_32BYTE) || compiler->compOpportunisticallyDependsOn(InstructionSet_AVX2))
+ {
#if defined(FEATURE_SIMD)
- emit->emitIns_SIMD_R_R_R(INS_pcmpeqd, attr, targetReg, targetReg, targetReg);
+ emit->emitIns_SIMD_R_R_R(INS_pcmpeqd, attr, targetReg, targetReg, targetReg);
#else
- emit->emitIns_R_R(INS_pcmpeqd, attr, targetReg, targetReg);
+ emit->emitIns_R_R(INS_pcmpeqd, attr, targetReg, targetReg);
#endif // FEATURE_SIMD
- break;
+ break;
+ }
}
if (vecCon->IsZero())
{
+ if ((attr != EA_32BYTE) || compiler->compOpportunisticallyDependsOn(InstructionSet_AVX))
+ {
#if defined(FEATURE_SIMD)
- emit->emitIns_SIMD_R_R_R(INS_xorps, attr, targetReg, targetReg, targetReg);
+ emit->emitIns_SIMD_R_R_R(INS_xorps, attr, targetReg, targetReg, targetReg);
#else
- emit->emitIns_R_R(INS_xorps, attr, targetReg, targetReg);
+ emit->emitIns_R_R(INS_xorps, attr, targetReg, targetReg);
#endif // FEATURE_SIMD
- break;
+ break;
+ }
}
switch (tree->TypeGet())
@@ -3052,21 +3059,19 @@ void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode)
#ifdef FEATURE_PUT_STRUCT_ARG_STK
// Generate code for a load from some address + offset
-// baseNode: tree node which can be either a local address or arbitrary node
-// offset: distance from the baseNode from which to load
-void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* baseNode, unsigned offset)
+// base: tree node which can be either a local or an indir
+// offset: distance from the "base" location from which to load
+//
+void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset)
{
- emitter* emit = GetEmitter();
-
- if (baseNode->OperIsLocalAddr())
+ if (base->OperIsLocalRead())
{
- const GenTreeLclVarCommon* lclVar = baseNode->AsLclVarCommon();
- offset += lclVar->GetLclOffs();
- emit->emitIns_R_S(ins, size, dst, lclVar->GetLclNum(), offset);
+ GetEmitter()->emitIns_R_S(ins, size, dst, base->AsLclVarCommon()->GetLclNum(),
+ offset + base->AsLclVarCommon()->GetLclOffs());
}
else
{
- emit->emitIns_R_AR(ins, size, dst, baseNode->GetRegNum(), offset);
+ GetEmitter()->emitIns_R_AR(ins, size, dst, base->AsIndir()->Addr()->GetRegNum(), offset);
}
}
#endif // FEATURE_PUT_STRUCT_ARG_STK
@@ -3337,7 +3342,7 @@ void CodeGen::genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode)
// Arguments:
// size - The size of bytes remaining to be moved
// longTmpReg - The tmp register to be used for the long value
-// srcAddr - The address of the source struct
+// src - The source struct node (LCL/OBJ)
// offset - The current offset being copied
//
// Return Value:
@@ -3349,7 +3354,7 @@ void CodeGen::genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode)
// On x86, longTmpReg must be an xmm reg; on x64 it must be an integer register.
// This is checked by genStoreRegToStackArg.
//
-unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* srcAddr, unsigned offset)
+unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* src, unsigned offset)
{
#ifdef TARGET_X86
instruction longMovIns = INS_movq;
@@ -3358,7 +3363,7 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree*
#endif // !TARGET_X86
if ((size & 8) != 0)
{
- genCodeForLoadOffset(longMovIns, EA_8BYTE, longTmpReg, srcAddr, offset);
+ genCodeForLoadOffset(longMovIns, EA_8BYTE, longTmpReg, src, offset);
genStoreRegToStackArg(TYP_LONG, longTmpReg, offset);
return 8;
}
@@ -3371,7 +3376,7 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree*
// Arguments:
// size - The size of bytes remaining to be moved
// intTmpReg - The tmp register to be used for the long value
-// srcAddr - The address of the source struct
+// src - The source struct node (LCL/OBJ)
// offset - The current offset being copied
//
// Return Value:
@@ -3383,11 +3388,11 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree*
// intTmpReg must be an integer register.
// This is checked by genStoreRegToStackArg.
//
-unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset)
+unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset)
{
if ((size & 4) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_4BYTE, intTmpReg, srcAddr, offset);
+ genCodeForLoadOffset(INS_mov, EA_4BYTE, intTmpReg, src, offset);
genStoreRegToStackArg(TYP_INT, intTmpReg, offset);
return 4;
}
@@ -3400,7 +3405,7 @@ unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree*
// Arguments:
// size - The size of bytes remaining to be moved
// intTmpReg - The tmp register to be used for the long value
-// srcAddr - The address of the source struct
+// src - The source struct node (LCL/OBJ)
// offset - The current offset being copied
//
// Return Value:
@@ -3412,11 +3417,11 @@ unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree*
// intTmpReg must be an integer register.
// This is checked by genStoreRegToStackArg.
//
-unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset)
+unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset)
{
if ((size & 2) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_2BYTE, intTmpReg, srcAddr, offset);
+ genCodeForLoadOffset(INS_mov, EA_2BYTE, intTmpReg, src, offset);
genStoreRegToStackArg(TYP_SHORT, intTmpReg, offset);
return 2;
}
@@ -3429,7 +3434,7 @@ unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree*
// Arguments:
// size - The size of bytes remaining to be moved
// intTmpReg - The tmp register to be used for the long value
-// srcAddr - The address of the source struct
+// src - The source struct node (LCL/OBJ)
// offset - The current offset being copied
//
// Return Value:
@@ -3441,11 +3446,11 @@ unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree*
// intTmpReg must be an integer register.
// This is checked by genStoreRegToStackArg.
//
-unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset)
+unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset)
{
if ((size & 1) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_1BYTE, intTmpReg, srcAddr, offset);
+ genCodeForLoadOffset(INS_mov, EA_1BYTE, intTmpReg, src, offset);
genStoreRegToStackArg(TYP_BYTE, intTmpReg, offset);
return 1;
}
@@ -3469,37 +3474,36 @@ unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree*
//
void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode)
{
- GenTree* src = putArgNode->AsOp()->gtOp1;
- // We will never call this method for SIMD types, which are stored directly
- // in genPutStructArgStk().
- assert(src->isContained() && src->OperIs(GT_OBJ) && src->TypeIs(TYP_STRUCT));
- assert(!src->AsObj()->GetLayout()->HasGCPtr());
+ GenTree* src = putArgNode->Data();
+ // We will never call this method for SIMD types, which are stored directly in genPutStructArgStk().
+ assert(src->isContained() && src->TypeIs(TYP_STRUCT) && (src->OperIs(GT_OBJ) || src->OperIsLocalRead()));
+
#ifdef TARGET_X86
assert(!m_pushStkArg);
#endif
- unsigned size = putArgNode->GetStackByteSize();
-#ifdef TARGET_X86
- assert((XMM_REGSIZE_BYTES <= size) && (size <= CPBLK_UNROLL_LIMIT));
-#else // !TARGET_X86
- assert(size <= CPBLK_UNROLL_LIMIT);
-#endif // !TARGET_X86
-
- if (src->AsOp()->gtOp1->isUsedFromReg())
+ if (src->OperIs(GT_OBJ))
{
- genConsumeReg(src->AsOp()->gtOp1);
+ genConsumeReg(src->AsObj()->Addr());
}
+ unsigned loadSize = putArgNode->GetArgLoadSize();
+ assert(!src->GetLayout(compiler)->HasGCPtr() && (loadSize <= CPBLK_UNROLL_LIMIT));
+
unsigned offset = 0;
regNumber xmmTmpReg = REG_NA;
regNumber intTmpReg = REG_NA;
regNumber longTmpReg = REG_NA;
- if (size >= XMM_REGSIZE_BYTES)
+#ifdef TARGET_X86
+ if (loadSize >= 8)
+#else
+ if (loadSize >= XMM_REGSIZE_BYTES)
+#endif
{
xmmTmpReg = putArgNode->GetSingleTempReg(RBM_ALLFLOAT);
}
- if ((size % XMM_REGSIZE_BYTES) != 0)
+ if ((loadSize % XMM_REGSIZE_BYTES) != 0)
{
intTmpReg = putArgNode->GetSingleTempReg(RBM_ALLINT);
}
@@ -3511,7 +3515,7 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode)
#endif
// Let's use SSE2 to be able to do 16 byte at a time with loads and stores.
- size_t slots = size / XMM_REGSIZE_BYTES;
+ size_t slots = loadSize / XMM_REGSIZE_BYTES;
while (slots-- > 0)
{
// TODO: In the below code the load and store instructions are for 16 bytes, but the
@@ -3519,7 +3523,7 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode)
// this probably needs to be changed.
// Load
- genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmTmpReg, src->gtGetOp1(), offset);
+ genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmTmpReg, src, offset);
// Store
genStoreRegToStackArg(TYP_STRUCT, xmmTmpReg, offset);
@@ -3527,13 +3531,13 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode)
}
// Fill the remainder (15 bytes or less) if there's one.
- if ((size % XMM_REGSIZE_BYTES) != 0)
+ if ((loadSize % XMM_REGSIZE_BYTES) != 0)
{
- offset += genMove8IfNeeded(size, longTmpReg, src->AsOp()->gtOp1, offset);
- offset += genMove4IfNeeded(size, intTmpReg, src->AsOp()->gtOp1, offset);
- offset += genMove2IfNeeded(size, intTmpReg, src->AsOp()->gtOp1, offset);
- offset += genMove1IfNeeded(size, intTmpReg, src->AsOp()->gtOp1, offset);
- assert(offset == size);
+ offset += genMove8IfNeeded(loadSize, longTmpReg, src, offset);
+ offset += genMove4IfNeeded(loadSize, intTmpReg, src, offset);
+ offset += genMove2IfNeeded(loadSize, intTmpReg, src, offset);
+ offset += genMove1IfNeeded(loadSize, intTmpReg, src, offset);
+ assert(offset == loadSize);
}
}
@@ -3549,8 +3553,7 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode)
void CodeGen::genStructPutArgRepMovs(GenTreePutArgStk* putArgNode)
{
GenTree* src = putArgNode->gtGetOp1();
- assert(src->TypeGet() == TYP_STRUCT);
- assert(!src->AsObj()->GetLayout()->HasGCPtr());
+ assert(src->TypeIs(TYP_STRUCT) && !src->GetLayout(compiler)->HasGCPtr());
// Make sure we got the arguments of the cpblk operation in the right registers, and that
// 'src' is contained as expected.
@@ -3569,8 +3572,9 @@ void CodeGen::genStructPutArgRepMovs(GenTreePutArgStk* putArgNode)
// putArgNode - the PutArgStk tree.
//
// Notes:
-// Used only on x86, in two cases:
+// Used (only) on x86 for:
// - Structs 4, 8, or 12 bytes in size (less than XMM_REGSIZE_BYTES, multiple of TARGET_POINTER_SIZE).
+// - Local structs less than 16 bytes in size (it is ok to load "too much" from our stack frame).
// - Structs that contain GC pointers - they are guaranteed to be sized correctly by the VM.
//
void CodeGen::genStructPutArgPush(GenTreePutArgStk* putArgNode)
@@ -3583,42 +3587,37 @@ void CodeGen::genStructPutArgPush(GenTreePutArgStk* putArgNode)
// future.
assert(m_pushStkArg);
- GenTree* src = putArgNode->Data();
- GenTree* srcAddr = putArgNode->Data()->AsObj()->Addr();
-
- regNumber srcAddrReg = srcAddr->GetRegNum();
- const bool srcAddrInReg = srcAddrReg != REG_NA;
-
- unsigned srcLclNum = 0;
- unsigned srcLclOffset = 0;
- if (srcAddrInReg)
+ GenTree* src = putArgNode->Data();
+ regNumber srcAddrReg = REG_NA;
+ unsigned srcLclNum = BAD_VAR_NUM;
+ unsigned srcLclOffs = BAD_LCL_OFFSET;
+ if (src->OperIsLocalRead())
{
- srcAddrReg = genConsumeReg(srcAddr);
+ assert(src->isContained());
+ srcLclNum = src->AsLclVarCommon()->GetLclNum();
+ srcLclOffs = src->AsLclVarCommon()->GetLclOffs();
}
else
{
- assert(srcAddr->OperIsLocalAddr());
-
- srcLclNum = srcAddr->AsLclVarCommon()->GetLclNum();
- srcLclOffset = srcAddr->AsLclVarCommon()->GetLclOffs();
+ srcAddrReg = genConsumeReg(src->AsObj()->Addr());
}
- ClassLayout* layout = src->AsObj()->GetLayout();
- const unsigned byteSize = putArgNode->GetStackByteSize();
- assert((byteSize % TARGET_POINTER_SIZE == 0) && ((byteSize < XMM_REGSIZE_BYTES) || layout->HasGCPtr()));
- const unsigned numSlots = byteSize / TARGET_POINTER_SIZE;
+ ClassLayout* layout = src->GetLayout(compiler);
+ const unsigned loadSize = putArgNode->GetArgLoadSize();
+ assert(((loadSize < XMM_REGSIZE_BYTES) || layout->HasGCPtr()) && ((loadSize % TARGET_POINTER_SIZE) == 0));
+ const unsigned numSlots = loadSize / TARGET_POINTER_SIZE;
for (int i = numSlots - 1; i >= 0; --i)
{
emitAttr slotAttr = emitTypeSize(layout->GetGCPtrType(i));
const unsigned byteOffset = i * TARGET_POINTER_SIZE;
- if (srcAddrInReg)
+ if (srcAddrReg != REG_NA)
{
GetEmitter()->emitIns_AR_R(INS_push, slotAttr, REG_NA, srcAddrReg, byteOffset);
}
else
{
- GetEmitter()->emitIns_S(INS_push, slotAttr, srcLclNum, srcLclOffset + byteOffset);
+ GetEmitter()->emitIns_S(INS_push, slotAttr, srcLclNum, srcLclOffs + byteOffset);
}
AddStackLevel(TARGET_POINTER_SIZE);
@@ -3643,19 +3642,18 @@ void CodeGen::genStructPutArgPartialRepMovs(GenTreePutArgStk* putArgNode)
// They may now contain gc pointers (depending on their type; gcMarkRegPtrVal will "do the right thing").
genConsumePutStructArgStk(putArgNode, REG_RDI, REG_RSI, REG_NA);
- GenTreeObj* src = putArgNode->gtGetOp1()->AsObj();
- ClassLayout* layout = src->GetLayout();
- const bool srcIsLocal = src->Addr()->OperIsLocalAddr();
- const emitAttr srcAddrAttr = srcIsLocal ? EA_PTRSIZE : EA_BYREF;
+ GenTree* src = putArgNode->Data();
+ ClassLayout* layout = src->GetLayout(compiler);
+ const emitAttr srcAddrAttr = src->OperIsLocalRead() ? EA_PTRSIZE : EA_BYREF;
#if DEBUG
unsigned numGCSlotsCopied = 0;
#endif // DEBUG
assert(layout->HasGCPtr());
- const unsigned byteSize = putArgNode->GetStackByteSize();
- assert(byteSize % TARGET_POINTER_SIZE == 0);
- const unsigned numSlots = byteSize / TARGET_POINTER_SIZE;
+ const unsigned argSize = putArgNode->GetStackByteSize();
+ assert(argSize % TARGET_POINTER_SIZE == 0);
+ const unsigned numSlots = argSize / TARGET_POINTER_SIZE;
// No need to disable GC the way COPYOBJ does. Here the refs are copied in atomic operations always.
for (unsigned i = 0; i < numSlots;)
@@ -4763,7 +4761,8 @@ void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
unsigned varNum = tree->GetLclNum();
assert(varNum < compiler->lvaCount);
- GetEmitter()->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
+ instruction loadIns = tree->DontExtend() ? INS_mov : ins_Load(targetType);
+ GetEmitter()->emitIns_R_S(loadIns, size, targetReg, varNum, offs);
genProduceReg(tree);
}
@@ -5114,16 +5113,7 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)
else
{
genConsumeAddress(addr);
- instruction loadIns = ins_Load(targetType);
- if (tree->DontExtend())
- {
- assert(varTypeIsSmall(tree));
- // The user of this IND does not need
- // the upper bits to be set, so we don't need to use longer
- // INS_movzx/INS_movsx and can use INS_mov instead.
- // It usually happens when the real type is a small struct.
- loadIns = INS_mov;
- }
+ instruction loadIns = tree->DontExtend() ? INS_mov : ins_Load(targetType);
emit->emitInsLoadInd(loadIns, emitTypeSize(tree), tree->GetRegNum(), tree);
}
@@ -5524,23 +5514,17 @@ void CodeGen::genCall(GenTreeCall* call)
GenTree* argNode = arg.GetEarlyNode();
if (argNode->OperIs(GT_PUTARG_STK) && (arg.GetLateNode() == nullptr))
{
- GenTree* source = argNode->AsPutArgStk()->gtGetOp1();
- unsigned size = argNode->AsPutArgStk()->GetStackByteSize();
- stackArgBytes += size;
+ GenTree* source = argNode->AsPutArgStk()->gtGetOp1();
+ unsigned argSize = argNode->AsPutArgStk()->GetStackByteSize();
+ stackArgBytes += argSize;
+
#ifdef DEBUG
- assert(size == arg.AbiInfo.ByteSize);
+ assert(argSize == arg.AbiInfo.ByteSize);
#ifdef FEATURE_PUT_STRUCT_ARG_STK
- if (!source->OperIs(GT_FIELD_LIST) && (source->TypeGet() == TYP_STRUCT))
+ if (source->TypeIs(TYP_STRUCT) && !source->OperIs(GT_FIELD_LIST))
{
- GenTreeObj* obj = source->AsObj();
- unsigned argBytes = roundUp(obj->GetLayout()->GetSize(), TARGET_POINTER_SIZE);
-#ifdef TARGET_X86
- // If we have an OBJ, we must have created a copy if the original arg was not a
- // local and was not a multiple of TARGET_POINTER_SIZE.
- // Note that on x64/ux this will be handled by unrolling in genStructPutArgUnroll.
- assert((argBytes == obj->GetLayout()->GetSize()) || obj->Addr()->IsLocalAddrExpr());
-#endif // TARGET_X86
- assert(arg.AbiInfo.ByteSize == argBytes);
+ unsigned loadSize = source->GetLayout(compiler)->GetSize();
+ assert(argSize == roundUp(loadSize, TARGET_POINTER_SIZE));
}
#endif // FEATURE_PUT_STRUCT_ARG_STK
#endif // DEBUG
@@ -6746,11 +6730,14 @@ void CodeGen::genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& d
break;
case GenIntCastDesc::CHECK_INT_RANGE:
- GetEmitter()->emitIns_R_I(INS_cmp, EA_8BYTE, reg, INT32_MAX);
- genJumpToThrowHlpBlk(EJ_jg, SCK_OVERFLOW);
- GetEmitter()->emitIns_R_I(INS_cmp, EA_8BYTE, reg, INT32_MIN);
- genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
- break;
+ {
+ // Emit "if ((long)(int)x != x) goto OVERFLOW"
+ const regNumber regTmp = cast->GetSingleTempReg();
+ GetEmitter()->emitIns_Mov(INS_movsxd, EA_8BYTE, regTmp, reg, true);
+ GetEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, regTmp);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
+ }
+ break;
#endif
default:
@@ -7796,11 +7783,11 @@ bool CodeGen::genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk)
{
case GenTreePutArgStk::Kind::RepInstr:
case GenTreePutArgStk::Kind::Unroll:
- assert(!source->AsObj()->GetLayout()->HasGCPtr());
+ assert(!source->GetLayout(compiler)->HasGCPtr());
break;
case GenTreePutArgStk::Kind::Push:
- assert(source->OperIs(GT_FIELD_LIST) || source->AsObj()->GetLayout()->HasGCPtr() ||
+ assert(source->OperIs(GT_FIELD_LIST) || source->GetLayout(compiler)->HasGCPtr() ||
(argSize < XMM_REGSIZE_BYTES));
break;
@@ -8314,14 +8301,11 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk)
assert(targetType == TYP_STRUCT);
- ClassLayout* layout = source->AsObj()->GetLayout();
-
switch (putArgStk->gtPutArgStkKind)
{
case GenTreePutArgStk::Kind::RepInstr:
genStructPutArgRepMovs(putArgStk);
break;
-
#ifndef TARGET_X86
case GenTreePutArgStk::Kind::PartialRepInstr:
genStructPutArgPartialRepMovs(putArgStk);
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 2201c3a7d1e8d9..460412d84597e2 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -3199,10 +3199,10 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
opts.compProcedureSplitting = jitFlags->IsSet(JitFlags::JIT_FLAG_PROCSPLIT) || enableFakeSplitting;
-#ifdef TARGET_ARM64
- // TODO-ARM64-NYI: enable hot/cold splitting
+#ifdef TARGET_LOONGARCH64
+ // Hot/cold splitting is not being tested on LoongArch64.
opts.compProcedureSplitting = false;
-#endif // TARGET_ARM64
+#endif // TARGET_LOONGARCH64
#ifdef DEBUG
opts.compProcedureSplittingEH = opts.compProcedureSplitting;
@@ -5199,25 +5199,41 @@ void Compiler::placeLoopAlignInstructions()
{
// Loop alignment is disabled for cold blocks
assert((block->bbFlags & BBF_COLD) == 0);
+ BasicBlock* const loopTop = block->bbNext;
// If jmp was not found, then block before the loop start is where align instruction will be added.
+ //
if (bbHavingAlign == nullptr)
{
- bbHavingAlign = block;
- JITDUMP("Marking " FMT_BB " before the loop with BBF_HAS_ALIGN for loop at " FMT_BB "\n", block->bbNum,
- block->bbNext->bbNum);
+ // In some odd cases we may see blocks within the loop before we see the
+ // top block of the loop. Just bail on aligning such loops.
+ //
+ if ((block->bbNatLoopNum != BasicBlock::NOT_IN_LOOP) && (block->bbNatLoopNum == loopTop->bbNatLoopNum))
+ {
+ loopTop->unmarkLoopAlign(this DEBUG_ARG("loop block appears before top of loop"));
+ }
+ else
+ {
+ bbHavingAlign = block;
+ JITDUMP("Marking " FMT_BB " before the loop with BBF_HAS_ALIGN for loop at " FMT_BB "\n",
+ block->bbNum, loopTop->bbNum);
+ }
}
else
{
JITDUMP("Marking " FMT_BB " that ends with unconditional jump with BBF_HAS_ALIGN for loop at " FMT_BB
"\n",
- bbHavingAlign->bbNum, block->bbNext->bbNum);
+ bbHavingAlign->bbNum, loopTop->bbNum);
+ }
+
+ if (bbHavingAlign != nullptr)
+ {
+ bbHavingAlign->bbFlags |= BBF_HAS_ALIGN;
}
- bbHavingAlign->bbFlags |= BBF_HAS_ALIGN;
minBlockSoFar = BB_MAX_WEIGHT;
bbHavingAlign = nullptr;
- currentAlignedLoopNum = block->bbNext->bbNatLoopNum;
+ currentAlignedLoopNum = loopTop->bbNatLoopNum;
if (--loopsToProcess == 0)
{
@@ -6384,10 +6400,10 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
compHndBBtabCount = 0;
compHndBBtabAllocCount = 0;
- info.compNativeCodeSize = 0;
- info.compTotalHotCodeSize = 0;
- info.compTotalColdCodeSize = 0;
- info.compClassProbeCount = 0;
+ info.compNativeCodeSize = 0;
+ info.compTotalHotCodeSize = 0;
+ info.compTotalColdCodeSize = 0;
+ info.compHandleHistogramProbeCount = 0;
compHasBackwardJump = false;
compHasBackwardJumpInHandler = false;
@@ -6563,7 +6579,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
{
// This looks like a viable inline candidate. Since
// we're not actually inlining, don't report anything.
- prejitResult.SetReported();
+ prejitResult.SetSuccessResult(INLINE_PREJIT_SUCCESS);
}
}
else
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index b4c7eb375b4d22..b5bfdf926f2126 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -477,9 +477,9 @@ class LclVarDsc
unsigned char lvIsTemp : 1; // Short-lifetime compiler temp
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
unsigned char lvIsImplicitByRef : 1; // Set if the argument is an implicit byref.
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#endif // FEATURE_IMPLICIT_BYREFS
#if defined(TARGET_LOONGARCH64)
unsigned char lvIs4Field1 : 1; // Set if the 1st field is int or float within struct for LA-ABI64.
@@ -614,6 +614,8 @@ class LclVarDsc
unsigned char lvSingleDefDisqualifyReason = 'H';
#endif
+ unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc
+
#if FEATURE_MULTIREG_ARGS
regNumber lvRegNumForSlot(unsigned slotNum)
{
@@ -1015,13 +1017,8 @@ class LclVarDsc
return NO_CLASS_HANDLE;
}
#endif
- assert(m_layout != nullptr);
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
- assert(varTypeIsStruct(TypeGet()) || (lvIsImplicitByRef && (TypeGet() == TYP_BYREF)));
-#else
- assert(varTypeIsStruct(TypeGet()));
-#endif
- CORINFO_CLASS_HANDLE structHnd = m_layout->GetClassHandle();
+
+ CORINFO_CLASS_HANDLE structHnd = GetLayout()->GetClassHandle();
assert(structHnd != NO_CLASS_HANDLE);
return structHnd;
}
@@ -1091,10 +1088,14 @@ class LclVarDsc
return varTypeIsGC(lvType) || ((lvType == TYP_STRUCT) && m_layout->HasGCPtr());
}
- // Returns the layout of a struct variable.
+ // Returns the layout of a struct variable or implicit byref.
ClassLayout* GetLayout() const
{
- assert(varTypeIsStruct(lvType));
+#if FEATURE_IMPLICIT_BYREFS
+ assert(varTypeIsStruct(TypeGet()) || (lvIsImplicitByRef && (TypeGet() == TYP_BYREF)));
+#else
+ assert(varTypeIsStruct(TypeGet()));
+#endif
return m_layout;
}
@@ -1242,6 +1243,16 @@ class IntegralRange
assert(lowerBound <= upperBound);
}
+ SymbolicIntegerValue GetLowerBound()
+ {
+ return m_lowerBound;
+ }
+
+ SymbolicIntegerValue GetUpperBound()
+ {
+ return m_upperBound;
+ }
+
bool Contains(int64_t value) const;
bool Contains(IntegralRange other) const
@@ -1820,6 +1831,7 @@ class Compiler
friend class MorphInitBlockHelper;
friend class MorphCopyBlockHelper;
friend class CallArgs;
+ friend class IndirectCallTransformer;
#ifdef FEATURE_HW_INTRINSICS
friend struct HWIntrinsicInfo;
@@ -1882,11 +1894,6 @@ class Compiler
//
bool IsHfa(CORINFO_CLASS_HANDLE hClass);
- bool IsHfa(GenTree* tree);
-
- var_types GetHfaType(GenTree* tree);
- unsigned GetHfaCount(GenTree* tree);
-
var_types GetHfaType(CORINFO_CLASS_HANDLE hClass);
unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass);
@@ -2269,7 +2276,7 @@ class Compiler
GenTree* gtNewIndOfIconHandleNode(var_types indType, size_t value, GenTreeFlags iconFlags, bool isInvariant);
- GenTree* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields = nullptr);
+ GenTreeIntCon* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields = nullptr);
GenTreeFlags gtTokenToIconFlags(unsigned token);
@@ -3286,22 +3293,8 @@ class Compiler
}
#endif // TARGET_X86
- // For x64 this is 3, 5, 6, 7, >8 byte structs that are passed by reference.
- // For ARM64, this is structs larger than 16 bytes that are passed by reference.
- bool lvaIsImplicitByRefLocal(unsigned varNum)
- {
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
- LclVarDsc* varDsc = lvaGetDesc(varNum);
- if (varDsc->lvIsImplicitByRef)
- {
- assert(varDsc->lvIsParam);
-
- assert(varTypeIsStruct(varDsc) || (varDsc->lvType == TYP_BYREF));
- return true;
- }
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
- return false;
- }
+ bool lvaIsImplicitByRefLocal(unsigned lclNum) const;
+ bool lvaIsLocalImplicitlyAccessedByRef(unsigned lclNum) const;
// Returns true if this local var is a multireg struct
bool lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVararg);
@@ -3557,6 +3550,18 @@ class Compiler
bool isExplicitTailCall,
IL_OFFSET ilOffset = BAD_IL_OFFSET);
+ bool impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset);
+
+ enum class GDVProbeType
+ {
+ None,
+ ClassProfile,
+ MethodProfile,
+ MethodAndClassProfile,
+ };
+
+ GDVProbeType compClassifyGDVProbeType(GenTreeCall* call);
+
//=========================================================================
// PROTECTED
//=========================================================================
@@ -3852,10 +3857,7 @@ class Compiler
var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoType* simdBaseJitType = nullptr);
- GenTree* impNormStructVal(GenTree* structVal,
- CORINFO_CLASS_HANDLE structHnd,
- unsigned curLevel,
- bool forceNormalization = false);
+ GenTree* impNormStructVal(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel);
GenTree* impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken,
bool* pRuntimeLookup = nullptr,
@@ -5374,13 +5376,6 @@ class Compiler
void* pCallBackData = nullptr,
bool computeStack = false);
- // An fgWalkPreFn that looks for expressions that have inline throws in
- // minopts mode. Basically it looks for tress with gtOverflowEx() or
- // GTF_IND_RNGCHK. It returns WALK_ABORT if one is found. It
- // returns WALK_SKIP_SUBTREES if GTF_EXCEPT is not set (assumes flags
- // properly propagated to parent trees). It returns WALK_CONTINUE
- // otherwise.
- static fgWalkResult fgChkThrowCB(GenTree** pTree, Compiler::fgWalkData* data);
static fgWalkResult fgChkLocAllocCB(GenTree** pTree, Compiler::fgWalkData* data);
static fgWalkResult fgChkQmarkCB(GenTree** pTree, Compiler::fgWalkData* data);
@@ -5445,7 +5440,7 @@ class Compiler
bool fgGetProfileWeightForBasicBlock(IL_OFFSET offset, weight_t* weight);
Instrumentor* fgCountInstrumentor;
- Instrumentor* fgClassInstrumentor;
+ Instrumentor* fgHistogramInstrumentor;
PhaseStatus fgPrepareToInstrumentMethod();
PhaseStatus fgInstrumentMethod();
@@ -5453,12 +5448,6 @@ class Compiler
void fgIncorporateBlockCounts();
void fgIncorporateEdgeCounts();
- CORINFO_CLASS_HANDLE getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
- UINT32 countSchemaItems,
- BYTE* pInstrumentationData,
- int32_t ilOffset,
- CLRRandom* random);
-
public:
const char* fgPgoFailReason;
bool fgPgoDisabled;
@@ -5649,7 +5638,10 @@ class Compiler
void fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg);
GenTree* fgMorphLocal(GenTreeLclVarCommon* lclNode);
+#ifdef TARGET_X86
GenTree* fgMorphExpandStackArgForVarArgs(GenTreeLclVarCommon* lclNode);
+#endif // TARGET_X86
+ GenTree* fgMorphExpandImplicitByRefArg(GenTreeLclVarCommon* lclNode);
GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph);
public:
@@ -5694,6 +5686,7 @@ class Compiler
Statement* paramAssignmentInsertionPoint);
GenTree* fgMorphCall(GenTreeCall* call);
GenTree* fgExpandVirtualVtableCallTarget(GenTreeCall* call);
+
void fgMorphCallInline(GenTreeCall* call, InlineResult* result);
void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result, InlineContext** createdContext);
#if DEBUG
@@ -5715,8 +5708,10 @@ class Compiler
GenTree* fgMorphForRegisterFP(GenTree* tree);
GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr);
GenTree* fgOptimizeCast(GenTreeCast* cast);
+ GenTree* fgOptimizeCastOnAssignment(GenTreeOp* asg);
GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp);
GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp);
+ GenTree* fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* cmp);
#ifdef FEATURE_HW_INTRINSICS
GenTree* fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node);
#endif
@@ -5863,10 +5858,6 @@ class Compiler
// promoted, create new promoted struct temps.
void fgRetypeImplicitByRefArgs();
- // Rewrite appearances of implicit byrefs (manifest the implied additional level of indirection).
- bool fgMorphImplicitByRefArgs(GenTree* tree);
- GenTree* fgMorphImplicitByRefArgs(GenTree* tree, bool isAddr);
-
// Clear up annotations for any struct promotion temps created for implicit byrefs.
void fgMarkDemotedImplicitByRefArgs();
@@ -5901,6 +5892,8 @@ class Compiler
bool gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper = nullptr);
bool gtIsActiveCSE_Candidate(GenTree* tree);
+ ExceptionSetFlags gtCollectExceptions(GenTree* tree);
+
bool fgIsBigOffset(size_t offset);
bool fgNeedReturnSpillTemp();
@@ -5936,13 +5929,11 @@ class Compiler
VNSet* m_pHoistedInCurLoop;
public:
- // Value numbers of expressions that have been hoisted in parent loops in the loop nest.
- VNSet m_hoistedInParentLoops;
-
// Value numbers of expressions that have been hoisted in the current (or most recent) loop in the nest.
// Previous decisions on loop-invariance of value numbers in the current loop.
VNSet m_curLoopVnInvariantCache;
+ // Get the VN cache for current loop
VNSet* GetHoistedInCurLoop(Compiler* comp)
{
if (m_pHoistedInCurLoop == nullptr)
@@ -5952,35 +5943,35 @@ class Compiler
return m_pHoistedInCurLoop;
}
- VNSet* ExtractHoistedInCurLoop()
+ // Return the so far collected VNs in cache for current loop and reset it.
+ void ResetHoistedInCurLoop()
{
- VNSet* res = m_pHoistedInCurLoop;
m_pHoistedInCurLoop = nullptr;
- return res;
+ JITDUMP("Resetting m_pHoistedInCurLoop\n");
}
LoopHoistContext(Compiler* comp)
- : m_pHoistedInCurLoop(nullptr)
- , m_hoistedInParentLoops(comp->getAllocatorLoopHoist())
- , m_curLoopVnInvariantCache(comp->getAllocatorLoopHoist())
+ : m_pHoistedInCurLoop(nullptr), m_curLoopVnInvariantCache(comp->getAllocatorLoopHoist())
{
}
};
- // Do hoisting for loop "lnum" (an index into the optLoopTable), and all loops nested within it.
- // Tracks the expressions that have been hoisted by containing loops by temporarily recording their
- // value numbers in "m_hoistedInParentLoops". This set is not modified by the call.
+ // Do hoisting of all loops nested within loop "lnum" (an index into the optLoopTable), followed
+ // by the loop "lnum" itself.
+ //
+ // "m_pHoistedInCurLoop" helps a lot in eliminating duplicate expressions getting hoisted
+ // and reducing the count of total expressions hoisted out of loop. When calculating the
+ // profitability, we compare this with number of registers and hence, lower the number of expressions
+ // getting hoisted, better chances that they will get enregistered and CSE considering them.
+ //
void optHoistLoopNest(unsigned lnum, LoopHoistContext* hoistCtxt);
// Do hoisting for a particular loop ("lnum" is an index into the optLoopTable.)
- // Assumes that expressions have been hoisted in containing loops if their value numbers are in
- // "m_hoistedInParentLoops".
- //
- void optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt);
+ // Returns the new preheaders created.
+ void optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt, BasicBlockList* existingPreHeaders);
// Hoist all expressions in "blocks" that are invariant in loop "loopNum" (an index into the optLoopTable)
- // outside of that loop. Exempt expressions whose value number is in "m_hoistedInParentLoops"; add VN's of hoisted
- // expressions to "hoistInLoop".
+ // outside of that loop.
void optHoistLoopBlocks(unsigned loopNum, ArrayStack* blocks, LoopHoistContext* hoistContext);
// Return true if the tree looks profitable to hoist out of loop 'lnum'.
@@ -6357,12 +6348,23 @@ class Compiler
// unshared with any other loop. Returns "true" iff the flowgraph has been modified
bool optCanonicalizeLoop(unsigned char loopInd);
+ enum class LoopCanonicalizationOption
+ {
+ Outer,
+ Current
+ };
+
+ bool optCanonicalizeLoopCore(unsigned char loopInd, LoopCanonicalizationOption option);
+
// Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP".
// Requires "l2" to be a valid loop table index, or else "BasicBlock::NOT_IN_LOOP".
// Returns true iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2".
// A loop contains itself.
bool optLoopContains(unsigned l1, unsigned l2) const;
+ // Returns the lpEntry for given preheader block of a loop
+ BasicBlock* optLoopEntry(BasicBlock* preHeader);
+
// Updates the loop table by changing loop "loopInd", whose head is required
// to be "from", to be "to". Also performs this transformation for any
// loop nested in "loopInd" that shares the same head as "loopInd".
@@ -6815,13 +6817,21 @@ class Compiler
optMethodFlags |= OMF_HAS_GUARDEDDEVIRT;
}
+ void pickGDV(GenTreeCall* call,
+ IL_OFFSET ilOffset,
+ bool isInterface,
+ CORINFO_CLASS_HANDLE* classGuess,
+ CORINFO_METHOD_HANDLE* methodGuess,
+ unsigned* likelihood);
+
void considerGuardedDevirtualization(GenTreeCall* call,
IL_OFFSET ilOffset,
bool isInterface,
CORINFO_METHOD_HANDLE baseMethod,
CORINFO_CLASS_HANDLE baseClass,
- CORINFO_CONTEXT_HANDLE* pContextHandle DEBUGARG(CORINFO_CLASS_HANDLE objClass)
- DEBUGARG(const char* objClassName));
+ CORINFO_CONTEXT_HANDLE* pContextHandle);
+
+ bool isCompatibleMethodGDV(GenTreeCall* call, CORINFO_METHOD_HANDLE gdvTarget);
void addGuardedDevirtualizationCandidate(GenTreeCall* call,
CORINFO_METHOD_HANDLE methodHandle,
@@ -7264,6 +7274,7 @@ class Compiler
GenTree* optConstantAssertionProp(AssertionDsc* curAssertion,
GenTreeLclVarCommon* tree,
Statement* stmt DEBUGARG(AssertionIndex index));
+ bool optIsProfitableToSubstitute(GenTreeLclVarCommon* lcl, BasicBlock* lclBlock, GenTree* value);
bool optZeroObjAssertionProp(GenTree* tree, ASSERT_VALARG_TP assertions);
// Assertion propagation functions.
@@ -7649,7 +7660,7 @@ class Compiler
// ICorJitInfo wrappers
- void eeAllocMem(AllocMemArgs* args);
+ void eeAllocMem(AllocMemArgs* args, const UNATIVE_OFFSET roDataSectionAlignment);
void eeReserveUnwindInfo(bool isFunclet, bool isColdCode, ULONG unwindSize);
@@ -8006,10 +8017,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode);
void unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode);
-#ifdef DEBUG
- void fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode);
-#endif // DEBUG
-
#endif // TARGET_AMD64 || (TARGET_X86 && FEATURE_EH_FUNCLETS)
UNATIVE_OFFSET unwindGetCurrentOffset(FuncInfoDsc* func);
@@ -9556,7 +9563,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
unsigned genCPU; // What CPU are we running on
// Number of class profile probes in this method
- unsigned compClassProbeCount;
+ unsigned compHandleHistogramProbeCount;
} info;
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index 2fe7c10574c6b0..f6fa0e2d946b64 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -902,9 +902,8 @@ inline GenTree* Compiler::gtNewLargeOperNode(genTreeOps oper, var_types type, Ge
* that may need to be fixed up).
*/
-inline GenTree* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields)
+inline GenTreeIntCon* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields)
{
- GenTree* node;
assert((flags & (GTF_ICON_HDL_MASK | GTF_ICON_FIELD_OFF)) != 0);
// Interpret "fields == NULL" as "not a field."
@@ -913,6 +912,7 @@ inline GenTree* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags,
fields = FieldSeqStore::NotAField();
}
+ GenTreeIntCon* node;
#if defined(LATE_DISASM)
node = new (this, LargeOpOpcode()) GenTreeIntCon(TYP_I_IMPL, value, fields DEBUGARG(/*largeNode*/ true));
#else
@@ -1123,19 +1123,16 @@ inline GenTreeField* Compiler::gtNewFieldRef(var_types type, CORINFO_FIELD_HANDL
LclVarDsc* varDsc = lvaGetDesc(obj->AsUnOp()->gtOp1->AsLclVarCommon());
varDsc->lvFieldAccessed = 1;
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
- // These structs are passed by reference and can easily become global
- // references if those references are exposed. We clear out
- // address-exposure information for these parameters when they are
- // converted into references in fgRetypeImplicitByRefArgs() so we do
- // not have the necessary information in morph to know if these
- // indirections are actually global references, so we have to be
- // conservative here.
- if (varDsc->lvIsParam)
+
+ if (lvaIsImplicitByRefLocal(lvaGetLclNum(varDsc)))
{
+ // These structs are passed by reference and can easily become global references if those
+ // references are exposed. We clear out address-exposure information for these parameters
+ // when they are converted into references in fgRetypeImplicitByRefArgs() so we do not have
+ // the necessary information in morph to know if these indirections are actually global
+ // references, so we have to be conservative here.
fieldNode->gtFlags |= GTF_GLOB_REF;
}
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
}
else
{
@@ -1370,6 +1367,7 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
{
case GT_CNS_INT:
AsIntCon()->gtFieldSeq = FieldSeqStore::NotAField();
+ INDEBUG(AsIntCon()->gtTargetHandle = 0);
break;
#if defined(TARGET_ARM)
case GT_MUL_LONG:
@@ -1879,10 +1877,10 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState
bool doubleWeight = lvIsTemp;
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
// and, for the time being, implicit byref params
doubleWeight |= lvIsImplicitByRef;
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#endif // FEATURE_IMPLICIT_BYREFS
if (doubleWeight && (weight * 2 > weight))
{
diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp
index 781afe9dfbf328..d3be8577df4b4a 100644
--- a/src/coreclr/jit/decomposelongs.cpp
+++ b/src/coreclr/jit/decomposelongs.cpp
@@ -931,7 +931,6 @@ GenTree* DecomposeLongs::DecomposeNeg(LIR::Use& use)
Range().InsertAfter(loResult, zero, hiAdjust, hiResult);
loResult->gtFlags |= GTF_SET_FLAGS;
- hiAdjust->gtFlags |= GTF_USE_FLAGS;
#elif defined(TARGET_ARM)
@@ -942,7 +941,6 @@ GenTree* DecomposeLongs::DecomposeNeg(LIR::Use& use)
Range().InsertAfter(loResult, hiResult);
loResult->gtFlags |= GTF_SET_FLAGS;
- hiResult->gtFlags |= GTF_USE_FLAGS;
#endif
@@ -997,7 +995,6 @@ GenTree* DecomposeLongs::DecomposeArith(LIR::Use& use)
if ((oper == GT_ADD) || (oper == GT_SUB))
{
loResult->gtFlags |= GTF_SET_FLAGS;
- hiResult->gtFlags |= GTF_USE_FLAGS;
if ((loResult->gtFlags & GTF_OVERFLOW) != 0)
{
diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp
index f8c437e3266946..d09bffa0a5e9a6 100644
--- a/src/coreclr/jit/ee_il_dll.cpp
+++ b/src/coreclr/jit/ee_il_dll.cpp
@@ -1122,34 +1122,64 @@ void Compiler::eeDispLineInfos()
* (e.g., host AMD64, target ARM64), then VM will get confused anyway.
*/
-void Compiler::eeAllocMem(AllocMemArgs* args)
+void Compiler::eeAllocMem(AllocMemArgs* args, const UNATIVE_OFFSET roDataSectionAlignment)
{
#ifdef DEBUG
- const UNATIVE_OFFSET hotSizeRequest = args->hotCodeSize;
- const UNATIVE_OFFSET coldSizeRequest = args->coldCodeSize;
- // Fake splitting implementation: place hot/cold code in contiguous section
- if (JitConfig.JitFakeProcedureSplitting() && (coldSizeRequest > 0))
+ // Fake splitting implementation: place hot/cold code in contiguous section.
+ UNATIVE_OFFSET coldCodeOffset = 0;
+ if (JitConfig.JitFakeProcedureSplitting() && (args->coldCodeSize > 0))
{
- args->hotCodeSize = hotSizeRequest + coldSizeRequest;
+ coldCodeOffset = args->hotCodeSize;
+ assert(coldCodeOffset > 0);
+ args->hotCodeSize += args->coldCodeSize;
args->coldCodeSize = 0;
}
-#endif
+
+#endif // DEBUG
+
+#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+
+ // For arm64/LoongArch64, we want to allocate JIT data always adjacent to code similar to what native compiler does.
+ // This way allows us to use a single `ldr` to access such data like float constant/jmp table.
+ // For LoongArch64 using `pcaddi + ld` to access such data.
+
+ UNATIVE_OFFSET roDataAlignmentDelta = 0;
+ if (args->roDataSize > 0)
+ {
+ roDataAlignmentDelta = AlignmentPad(args->hotCodeSize, roDataSectionAlignment);
+ }
+
+ const UNATIVE_OFFSET roDataOffset = args->hotCodeSize + roDataAlignmentDelta;
+ args->hotCodeSize = roDataOffset + args->roDataSize;
+ args->roDataSize = 0;
+
+#endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
info.compCompHnd->allocMem(args);
#ifdef DEBUG
- if (JitConfig.JitFakeProcedureSplitting() && (coldSizeRequest > 0))
- {
- // Fix up hot/cold code pointers
- args->coldCodeBlock = ((BYTE*)args->hotCodeBlock) + hotSizeRequest;
- args->coldCodeBlockRW = ((BYTE*)args->hotCodeBlockRW) + hotSizeRequest;
- // Reset args' hot/cold code sizes in case caller reads them later
- args->hotCodeSize = hotSizeRequest;
- args->coldCodeSize = coldSizeRequest;
+ if (JitConfig.JitFakeProcedureSplitting() && (coldCodeOffset > 0))
+ {
+ // Fix up cold code pointers. Cold section is adjacent to hot section.
+ assert(args->coldCodeBlock == nullptr);
+ assert(args->coldCodeBlockRW == nullptr);
+ args->coldCodeBlock = ((BYTE*)args->hotCodeBlock) + coldCodeOffset;
+ args->coldCodeBlockRW = ((BYTE*)args->hotCodeBlockRW) + coldCodeOffset;
}
-#endif
+
+#endif // DEBUG
+
+#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+
+ // Fix up data section pointers.
+ assert(args->roDataBlock == nullptr);
+ assert(args->roDataBlockRW == nullptr);
+ args->roDataBlock = ((BYTE*)args->hotCodeBlock) + roDataOffset;
+ args->roDataBlockRW = ((BYTE*)args->hotCodeBlockRW) + roDataOffset;
+
+#endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
}
void Compiler::eeReserveUnwindInfo(bool isFunclet, bool isColdCode, ULONG unwindSize)
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index ebd4f2120585af..eb846e06a0dc6b 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -3894,15 +3894,33 @@ void emitter::emitDispJumpList()
unsigned int jmpCount = 0;
for (instrDescJmp* jmp = emitJumpList; jmp != nullptr; jmp = jmp->idjNext)
{
- printf("IG%02u IN%04x %3s[%u] -> IG%02u %s\n", jmp->idjIG->igNum, jmp->idDebugOnlyInfo()->idNum,
- codeGen->genInsDisplayName(jmp), jmp->idCodeSize(),
- ((insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel))->igNum,
-#if defined(TARGET_XARCH)
- jmp->idjIsRemovableJmpCandidate ? " ; removal candidate" : ""
+ printf("IG%02u IN%04x %3s[%u]", jmp->idjIG->igNum, jmp->idDebugOnlyInfo()->idNum,
+ codeGen->genInsDisplayName(jmp), jmp->idCodeSize());
+
+ if (!jmp->idIsBound())
+ {
+
+#if defined(TARGET_ARM64)
+ if ((jmp->idInsFmt() == IF_LARGEADR) || (jmp->idInsFmt() == IF_LARGELDC))
+ {
+ printf(" -> %s", getRegName(jmp->idReg1()));
+ }
+ else
+ {
+ printf(" -> IG%02u", ((insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel))->igNum);
+ }
#else
- ""
-#endif
- );
+ printf(" -> IG%02u", ((insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel))->igNum);
+
+#if defined(TARGET_XARCH)
+ if (jmp->idjIsRemovableJmpCandidate)
+ {
+ printf(" ; removal candidate");
+ }
+#endif // TARGET_XARCH
+#endif // !TARGET_ARM64
+ }
+ printf("\n");
jmpCount += 1;
}
printf(" total jump count: %u\n", jmpCount);
@@ -4045,16 +4063,12 @@ void emitter::emitRecomputeIGoffsets()
//
// Arguments:
// handle - a constant value to display a comment for
+// cookie - the cookie stored with the handle
// flags - a flag that the describes the handle
//
-void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
+void emitter::emitDispCommentForHandle(size_t handle, size_t cookie, GenTreeFlags flag)
{
#ifdef DEBUG
- if (handle == 0)
- {
- return;
- }
-
#ifdef TARGET_XARCH
const char* commentPrefix = " ;";
#else
@@ -4062,8 +4076,35 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
#endif
flag &= GTF_ICON_HDL_MASK;
- const char* str = nullptr;
+ if (cookie != 0)
+ {
+ if (flag == GTF_ICON_FTN_ADDR)
+ {
+ const char* className = nullptr;
+ const char* methName =
+ emitComp->eeGetMethodName(reinterpret_cast(cookie), &className);
+ printf("%s code for %s:%s", commentPrefix, className, methName);
+ return;
+ }
+
+ if ((flag == GTF_ICON_STATIC_HDL) || (flag == GTF_ICON_STATIC_BOX_PTR))
+ {
+ const char* className = nullptr;
+ const char* fieldName =
+ emitComp->eeGetFieldName(reinterpret_cast(cookie), &className);
+ printf("%s %s for %s%s%s", commentPrefix, flag == GTF_ICON_STATIC_HDL ? "data" : "box", className,
+ className != nullptr ? ":" : "", fieldName);
+ return;
+ }
+ }
+
+ if (handle == 0)
+ {
+ return;
+ }
+
+ const char* str = nullptr;
if (flag == GTF_ICON_STR_HDL)
{
const WCHAR* wstr = emitComp->eeGetCPString(handle);
@@ -4103,8 +4144,6 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
{
str = emitComp->eeGetClassName(reinterpret_cast(handle));
}
-#ifndef TARGET_XARCH
- // These are less useful for xarch:
else if (flag == GTF_ICON_CONST_PTR)
{
str = "const ptr";
@@ -4133,11 +4172,6 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
{
str = "token handle";
}
- else
- {
- str = "unknown";
- }
-#endif // TARGET_XARCH
if (str != nullptr)
{
@@ -4527,7 +4561,6 @@ void emitter::emitJumpDistBind()
else if (emitIsUncondJump(jmp))
{
// Nothing to do; we don't shrink these.
- assert(jmp->idjShort);
ssz = JMP_SIZE_SMALL;
}
else if (emitIsLoadLabel(jmp))
@@ -6230,7 +6263,13 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
coldCodeBlock = nullptr;
- CorJitAllocMemFlag allocMemFlag = CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN;
+ // This restricts the data alignment to: 4, 8, 16, or 32 bytes
+ // Alignments greater than 32 would require VM support in ICorJitInfo::allocMem
+ uint32_t dataAlignment = emitConsDsc.alignment;
+ assert((dataSection::MIN_DATA_ALIGN <= dataAlignment) && (dataAlignment <= dataSection::MAX_DATA_ALIGN) &&
+ isPow2(dataAlignment));
+
+ uint32_t codeAlignment = TARGET_POINTER_SIZE;
#ifdef TARGET_X86
//
@@ -6250,14 +6289,14 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
const weight_t scenarioHotWeight = 256.0;
if (emitComp->fgCalledCount > (scenarioHotWeight * emitComp->fgProfileRunsCount()))
{
- allocMemFlag = CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN;
+ codeAlignment = 16;
}
}
else
{
if (emitTotalHotCodeSize <= 16)
{
- allocMemFlag = CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN;
+ codeAlignment = 16;
}
}
#endif
@@ -6269,61 +6308,46 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
if (emitComp->opts.OptimizationEnabled() && !emitComp->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) &&
(emitTotalHotCodeSize > 16) && emitComp->fgHasLoops)
{
- allocMemFlag = CORJIT_ALLOCMEM_FLG_32BYTE_ALIGN;
+ codeAlignment = 32;
}
#endif
- // This restricts the emitConsDsc.alignment to: 1, 2, 4, 8, 16, or 32 bytes
- // Alignments greater than 32 would require VM support in ICorJitInfo::allocMem
- assert(isPow2(emitConsDsc.alignment) && (emitConsDsc.alignment <= 32));
+#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+ // For arm64/LoongArch64, we're going to put the data in the code section. So make sure the code section has
+ // adequate alignment.
+ if (emitConsDsc.dsdOffs > 0)
+ {
+ codeAlignment = max(codeAlignment, dataAlignment);
+ }
+#endif
- if (emitConsDsc.alignment == 16)
+ // Note that we don't support forcing code alignment of 8 bytes on 32-bit platforms; an omission?
+ assert((TARGET_POINTER_SIZE <= codeAlignment) && (codeAlignment <= 32) && isPow2(codeAlignment));
+
+ CorJitAllocMemFlag allocMemFlagCodeAlign = CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN;
+ if (codeAlignment == 32)
{
- allocMemFlag = static_cast(allocMemFlag | CORJIT_ALLOCMEM_FLG_RODATA_16BYTE_ALIGN);
+ allocMemFlagCodeAlign = CORJIT_ALLOCMEM_FLG_32BYTE_ALIGN;
}
- else if (emitConsDsc.alignment == 32)
+ else if (codeAlignment == 16)
{
- allocMemFlag = static_cast(allocMemFlag | CORJIT_ALLOCMEM_FLG_RODATA_32BYTE_ALIGN);
+ allocMemFlagCodeAlign = CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN;
}
- AllocMemArgs args;
- memset(&args, 0, sizeof(args));
-
-#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
- // For arm64/LoongArch64, we want to allocate JIT data always adjacent to code similar to what native compiler does.
- // This way allows us to use a single `ldr` to access such data like float constant/jmp table.
- // For LoongArch64 using `pcaddi + ld` to access such data.
- if (emitTotalColdCodeSize > 0)
+ CorJitAllocMemFlag allocMemFlagDataAlign = static_cast(0);
+ if (dataAlignment == 16)
{
- // JIT data might be far away from the cold code.
- NYI("Need to handle fix-up to data from cold code.");
+ allocMemFlagDataAlign = CORJIT_ALLOCMEM_FLG_RODATA_16BYTE_ALIGN;
}
-
- UNATIVE_OFFSET roDataAlignmentDelta = 0;
- if (emitConsDsc.dsdOffs && (emitConsDsc.alignment == TARGET_POINTER_SIZE))
+ else if (dataAlignment == 32)
{
- UNATIVE_OFFSET roDataAlignment = TARGET_POINTER_SIZE; // 8 Byte align by default.
- roDataAlignmentDelta = (UNATIVE_OFFSET)ALIGN_UP(emitTotalHotCodeSize, roDataAlignment) - emitTotalHotCodeSize;
- assert((roDataAlignmentDelta == 0) || (roDataAlignmentDelta == 4));
+ allocMemFlagDataAlign = CORJIT_ALLOCMEM_FLG_RODATA_32BYTE_ALIGN;
}
- args.hotCodeSize = emitTotalHotCodeSize + roDataAlignmentDelta + emitConsDsc.dsdOffs;
- args.coldCodeSize = emitTotalColdCodeSize;
- args.roDataSize = 0;
- args.xcptnsCount = xcptnsCount;
- args.flag = allocMemFlag;
-
- emitComp->eeAllocMem(&args);
-
- codeBlock = (BYTE*)args.hotCodeBlock;
- codeBlockRW = (BYTE*)args.hotCodeBlockRW;
- coldCodeBlock = (BYTE*)args.coldCodeBlock;
- coldCodeBlockRW = (BYTE*)args.coldCodeBlockRW;
-
- consBlock = codeBlock + emitTotalHotCodeSize + roDataAlignmentDelta;
- consBlockRW = codeBlockRW + emitTotalHotCodeSize + roDataAlignmentDelta;
+ CorJitAllocMemFlag allocMemFlag = static_cast(allocMemFlagCodeAlign | allocMemFlagDataAlign);
-#else
+ AllocMemArgs args;
+ memset(&args, 0, sizeof(args));
args.hotCodeSize = emitTotalHotCodeSize;
args.coldCodeSize = emitTotalColdCodeSize;
@@ -6331,7 +6355,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
args.xcptnsCount = xcptnsCount;
args.flag = allocMemFlag;
- emitComp->eeAllocMem(&args);
+ emitComp->eeAllocMem(&args, emitConsDsc.alignment);
codeBlock = (BYTE*)args.hotCodeBlock;
codeBlockRW = (BYTE*)args.hotCodeBlockRW;
@@ -6340,13 +6364,27 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
consBlock = (BYTE*)args.roDataBlock;
consBlockRW = (BYTE*)args.roDataBlockRW;
-#endif
-
#ifdef DEBUG
if ((allocMemFlag & CORJIT_ALLOCMEM_FLG_32BYTE_ALIGN) != 0)
{
assert(((size_t)codeBlock & 31) == 0);
}
+#if 0
+ // TODO: we should be able to assert the following, but it appears crossgen2 doesn't respect them,
+ // or maybe it respects them in the written image but not in the buffer pointer given to the JIT.
+ if ((allocMemFlag & CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN) != 0)
+ {
+ assert(((size_t)codeBlock & 15) == 0);
+ }
+ if ((allocMemFlag & CORJIT_ALLOCMEM_FLG_RODATA_32BYTE_ALIGN) != 0)
+ {
+ assert(((size_t)consBlock & 31) == 0);
+ }
+ if ((allocMemFlag & CORJIT_ALLOCMEM_FLG_RODATA_16BYTE_ALIGN) != 0)
+ {
+ assert(((size_t)consBlock & 15) == 0);
+ }
+#endif // 0
#endif
// if (emitConsDsc.dsdOffs)
@@ -7245,7 +7283,12 @@ UNATIVE_OFFSET emitter::emitDataGenBeg(unsigned size, unsigned alignment, var_ty
}
assert((secOffs % alignment) == 0);
- emitConsDsc.alignment = max(emitConsDsc.alignment, alignment);
+ if (emitConsDsc.alignment < alignment)
+ {
+ JITDUMP("Increasing data section alignment from %u to %u for type %s\n", emitConsDsc.alignment, alignment,
+ varTypeName(dataType));
+ emitConsDsc.alignment = alignment;
+ }
/* Advance the current offset */
emitConsDsc.dsdOffs += size;
@@ -7643,7 +7686,7 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst)
if (emitComp->opts.disAsm)
{
- emitDispDataSec(sec);
+ emitDispDataSec(sec, dst);
}
unsigned secNum = 0;
@@ -7764,13 +7807,14 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst)
//
// Arguments:
// section - the data section description
+// dst - address of the data section
//
// Notes:
// The output format attempts to mirror typical assembler syntax.
// Data section entries lack type information so float/double entries
// are displayed as if they are integers/longs.
//
-void emitter::emitDispDataSec(dataSecDsc* section)
+void emitter::emitDispDataSec(dataSecDsc* section, BYTE* dst)
{
printf("\n");
@@ -7778,11 +7822,17 @@ void emitter::emitDispDataSec(dataSecDsc* section)
for (dataSection* data = section->dsdList; data != nullptr; data = data->dsNext)
{
+ if (emitComp->opts.disAddr)
+ {
+ printf("; @" FMT_ADDR "\n", DBG_ADDR(dst));
+ }
+
const char* labelFormat = "%-7s";
char label[64];
sprintf_s(label, ArrLen(label), "RWD%02u", offset);
printf(labelFormat, label);
offset += data->dsSize;
+ dst += data->dsSize;
if ((data->dsType == dataSection::blockRelative32) || (data->dsType == dataSection::blockAbsoluteAddr))
{
diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h
index fc90bd96d6909e..d44fd1bd572eea 100644
--- a/src/coreclr/jit/emit.h
+++ b/src/coreclr/jit/emit.h
@@ -523,7 +523,7 @@ class emitter
void emitRecomputeIGoffsets();
- void emitDispCommentForHandle(size_t handle, GenTreeFlags flags);
+ void emitDispCommentForHandle(size_t handle, size_t cookie, GenTreeFlags flags);
/************************************************************************/
/* The following describes a single instruction */
@@ -554,7 +554,7 @@ class emitter
#endif // TARGET_XARCH
-#ifdef DEBUG // This information is used in DEBUG builds to display the method name for call instructions
+#ifdef DEBUG // This information is used in DEBUG builds for additional diagnostics
struct instrDesc;
@@ -997,7 +997,7 @@ class emitter
case IF_LARGELDC:
if (isVectorRegister(idReg1()))
{
- // adrp + ldr + fmov
+ // (adrp + ldr + fmov) or (adrp + add + ld1)
size = 12;
}
else
@@ -2555,7 +2555,7 @@ class emitter
void emitOutputDataSec(dataSecDsc* sec, BYTE* dst);
#ifdef DEBUG
- void emitDispDataSec(dataSecDsc* section);
+ void emitDispDataSec(dataSecDsc* section, BYTE* dst);
#endif
/************************************************************************/
diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp
index ccf3ddff357099..5e172a0914a6de 100644
--- a/src/coreclr/jit/emitarm.cpp
+++ b/src/coreclr/jit/emitarm.cpp
@@ -7678,6 +7678,62 @@ void emitter::emitDispInsHelp(
printf("\n");
}
+/*****************************************************************************
+ *
+ * Handles printing of LARGEJMP pseudo-instruction.
+ */
+
+void emitter::emitDispLargeJmp(
+ instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* code, size_t sz, insGroup* ig)
+{
+ // Note: don't touch the actual instrDesc. If we accidentally messed it up, it would create a very
+ // difficult to find bug.
+
+ instrDescJmp idJmp;
+ instrDescJmp* pidJmp = &idJmp;
+
+ memset(&idJmp, 0, sizeof(idJmp));
+
+ pidJmp->idIns(emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(id->idIns())))); // reverse the
+ // conditional
+ // instruction
+ pidJmp->idInsFmt(IF_T1_K);
+ pidJmp->idInsSize(emitInsSize(IF_T1_K));
+ pidJmp->idjShort = 1;
+ pidJmp->idAddr()->iiaSetInstrCount(1);
+ pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field
+
+ size_t bcondSizeOrZero = (code == NULL) ? 0 : 2; // branch is 2 bytes
+ emitDispInsHelp(pidJmp, false, doffs, asmfm, offset, code, bcondSizeOrZero,
+ NULL /* force display of pc-relative branch */);
+
+ code += bcondSizeOrZero;
+ offset += 2;
+
+ // Next, display the unconditional branch
+
+ // Reset the local instrDesc
+ memset(&idJmp, 0, sizeof(idJmp));
+
+ pidJmp->idIns(INS_b);
+ pidJmp->idInsFmt(IF_T2_J2);
+ pidJmp->idInsSize(emitInsSize(IF_T2_J2));
+ pidJmp->idjShort = 0;
+ if (id->idIsBound())
+ {
+ pidJmp->idSetIsBound();
+ pidJmp->idAddr()->iiaIGlabel = id->idAddr()->iiaIGlabel;
+ }
+ else
+ {
+ pidJmp->idAddr()->iiaBBlabel = id->idAddr()->iiaBBlabel;
+ }
+ pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field
+
+ size_t brSizeOrZero = (code == NULL) ? 0 : 4; // unconditional branch is 4 bytes
+ emitDispInsHelp(pidJmp, isNew, doffs, asmfm, offset, code, brSizeOrZero, ig);
+}
+
//--------------------------------------------------------------------
// emitDispIns: Dump the given instruction to jitstdout.
//
@@ -7714,53 +7770,7 @@ void emitter::emitDispIns(
//
// These instructions don't exist in the actual instruction stream, so we need to fake them
// up to display them.
- //
- // Note: don't touch the actual instrDesc. If we accidentally messed it up, it would create a very
- // difficult to find bug.
-
- instrDescJmp idJmp;
- instrDescJmp* pidJmp = &idJmp;
-
- memset(&idJmp, 0, sizeof(idJmp));
-
- pidJmp->idIns(emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(id->idIns())))); // reverse the
- // conditional
- // instruction
- pidJmp->idInsFmt(IF_T1_K);
- pidJmp->idInsSize(emitInsSize(IF_T1_K));
- pidJmp->idjShort = 1;
- pidJmp->idAddr()->iiaSetInstrCount(1);
- pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field
-
- size_t bcondSizeOrZero = (code == NULL) ? 0 : 2; // branch is 2 bytes
- emitDispInsHelp(pidJmp, false, doffs, asmfm, offset, code, bcondSizeOrZero,
- NULL /* force display of pc-relative branch */);
-
- code += bcondSizeOrZero;
- offset += 2;
-
- // Next, display the unconditional branch
-
- // Reset the local instrDesc
- memset(&idJmp, 0, sizeof(idJmp));
-
- pidJmp->idIns(INS_b);
- pidJmp->idInsFmt(IF_T2_J2);
- pidJmp->idInsSize(emitInsSize(IF_T2_J2));
- pidJmp->idjShort = 0;
- if (id->idIsBound())
- {
- pidJmp->idSetIsBound();
- pidJmp->idAddr()->iiaIGlabel = id->idAddr()->iiaIGlabel;
- }
- else
- {
- pidJmp->idAddr()->iiaBBlabel = id->idAddr()->iiaBBlabel;
- }
- pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field
-
- size_t brSizeOrZero = (code == NULL) ? 0 : 4; // unconditional branch is 4 bytes
- emitDispInsHelp(pidJmp, isNew, doffs, asmfm, offset, code, brSizeOrZero, ig);
+ emitDispLargeJmp(id, isNew, doffs, asmfm, offset, code, sz, ig);
}
else
{
diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h
index 09133a1d88f02d..c40c5fb85f9c51 100644
--- a/src/coreclr/jit/emitarm.h
+++ b/src/coreclr/jit/emitarm.h
@@ -43,6 +43,14 @@ void emitDispAddrRR(regNumber reg1, regNumber reg2, emitAttr attr);
void emitDispAddrRRI(regNumber reg1, regNumber reg2, int imm, emitAttr attr);
void emitDispAddrPUW(regNumber reg, int imm, insOpts opt, emitAttr attr);
void emitDispGC(emitAttr attr);
+void emitDispLargeJmp(instrDesc* id,
+ bool isNew,
+ bool doffs,
+ bool asmfm,
+ unsigned offs = 0,
+ BYTE* code = 0,
+ size_t sz = 0,
+ insGroup* ig = NULL);
void emitDispInsHelp(instrDesc* id,
bool isNew,
diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp
index 864ba862edddd7..b0bf1769d1bd09 100644
--- a/src/coreclr/jit/emitarm64.cpp
+++ b/src/coreclr/jit/emitarm64.cpp
@@ -3740,7 +3740,8 @@ void emitter::emitIns_R_I(instruction ins,
emitAttr attr,
regNumber reg,
ssize_t imm,
- insOpts opt /* = INS_OPTS_NONE */ DEBUGARG(GenTreeFlags gtFlags))
+ insOpts opt /* = INS_OPTS_NONE */
+ DEBUGARG(size_t targetHandle /* = 0 */) DEBUGARG(GenTreeFlags gtFlags /* = GTF_EMPTY */))
{
emitAttr size = EA_SIZE(attr);
emitAttr elemsize = EA_UNKNOWN;
@@ -3990,7 +3991,11 @@ void emitter::emitIns_R_I(instruction ins,
id->idInsOpt(opt);
id->idReg1(reg);
- INDEBUG(id->idDebugOnlyInfo()->idFlags = gtFlags);
+
+#ifdef DEBUG
+ id->idDebugOnlyInfo()->idMemCookie = targetHandle;
+ id->idDebugOnlyInfo()->idFlags = gtFlags;
+#endif
dispIns(id);
appendToCurIG(id);
@@ -4503,14 +4508,16 @@ void emitter::emitIns_R_R(
case INS_str:
case INS_strb:
case INS_strh:
-
- case INS_cmp:
case INS_cmn:
case INS_tst:
assert(insOptsNone(opt));
emitIns_R_R_I(ins, attr, reg1, reg2, 0, INS_OPTS_NONE);
return;
+ case INS_cmp:
+ emitIns_R_R_I(ins, attr, reg1, reg2, 0, opt);
+ return;
+
case INS_staddb:
emitIns_R_R_R(INS_ldaddb, attr, reg1, REG_ZR, reg2);
return;
@@ -4927,8 +4934,13 @@ void emitter::emitIns_R_R(
* Add an instruction referencing a register and two constants.
*/
-void emitter::emitIns_R_I_I(
- instruction ins, emitAttr attr, regNumber reg, ssize_t imm1, ssize_t imm2, insOpts opt /* = INS_OPTS_NONE */)
+void emitter::emitIns_R_I_I(instruction ins,
+ emitAttr attr,
+ regNumber reg,
+ ssize_t imm1,
+ ssize_t imm2,
+ insOpts opt /* = INS_OPTS_NONE */
+ DEBUGARG(size_t targetHandle /* = 0 */) DEBUGARG(GenTreeFlags gtFlags /* = 0 */))
{
emitAttr size = EA_SIZE(attr);
insFormat fmt = IF_NONE;
@@ -5015,6 +5027,11 @@ void emitter::emitIns_R_I_I(
id->idReg1(reg);
+#ifdef DEBUG
+ id->idDebugOnlyInfo()->idFlags = gtFlags;
+ id->idDebugOnlyInfo()->idMemCookie = targetHandle;
+#endif
+
dispIns(id);
appendToCurIG(id);
}
@@ -8421,10 +8438,12 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount)
switch (ins)
{
case INS_bl_local:
+ idjShort = true;
+ FALLTHROUGH;
case INS_b:
// Unconditional jump is a single form.
- idjShort = true;
- fmt = IF_BI_0A;
+ // Assume is long in case we cross hot/cold sections.
+ fmt = IF_BI_0A;
break;
case INS_beq:
@@ -8469,7 +8488,6 @@ void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount)
id->idAddr()->iiaBBlabel = dst;
// Skip unconditional jump that has a single form.
- // TODO-ARM64-NYI: enable hot/cold splittingNYI.
// The target needs to be relocated.
if (!idjShort)
{
@@ -9799,38 +9817,67 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i)
{
// Update addrReg with the reserved integer register
// since we cannot use dstReg (vector) to load constant directly from memory.
- addrReg = id->idReg2();
+
+ // If loading a 16-byte value, we will need to load directly into dstReg.
+ // Thus, encode addrReg for the ld1 instruction.
+ if (opSize == EA_16BYTE)
+ {
+ addrReg = encodingSPtoZR(id->idReg2());
+ }
+ else
+ {
+ addrReg = id->idReg2();
+ }
+
assert(isGeneralRegister(addrReg));
}
+
ins = INS_adrp;
fmt = IF_DI_1E;
dst = emitOutputShortAddress(dst, ins, fmt, relPageAddr, addrReg);
- // ldr x, [x, page offs] -- load constant from page address + page offset into integer register.
ssize_t imm12 = (ssize_t)dstAddr & 0xFFF; // 12 bits
assert(isValidUimm12(imm12));
- ins = INS_ldr;
- fmt = IF_LS_2B;
- dst = emitOutputShortConstant(dst, ins, fmt, imm12, addrReg, opSize);
- // fmov v, d -- copy constant in integer register to vector register.
- // This is needed only for vector constant.
- if (addrReg != dstReg)
+ // Special case: emit add + ld1 instructions for loading 16-byte data into vector register.
+ if (isVectorRegister(dstReg) && (opSize == EA_16BYTE))
{
- // fmov Vd,Rn DV_2I X00111100X100111 000000nnnnnddddd 1E27 0000 Vd,Rn
- // (scalar, from general)
- assert(isVectorRegister(dstReg) && isGeneralRegister(addrReg));
- ins = INS_fmov;
- fmt = IF_DV_2I;
- code_t code = emitInsCode(ins, fmt);
+ const emitAttr elemSize = EA_1BYTE;
+ const insOpts opt = optMakeArrangement(opSize, elemSize);
- code |= insEncodeReg_Vd(dstReg); // ddddd
- code |= insEncodeReg_Rn(addrReg); // nnnnn
- if (id->idOpSize() == EA_8BYTE)
+ assert(isGeneralRegisterOrSP(addrReg));
+ assert(isValidVectorElemsize(elemSize));
+ assert(isValidArrangement(opSize, opt));
+
+ // Calculate page addr + page offs, then emit ld1 instruction.
+ dst = emitOutputVectorConstant(dst, imm12, dstReg, addrReg, opSize, elemSize);
+ }
+ else
+ {
+ // ldr x, [x, 0] -- load constant from address into integer register.
+ ins = INS_ldr;
+ fmt = IF_LS_2B;
+ dst = emitOutputShortConstant(dst, ins, fmt, imm12, addrReg, opSize);
+
+ // fmov v, d -- copy constant in integer register to vector register.
+ // This is needed only for vector constant.
+ if (addrReg != dstReg)
{
- code |= 0x80400000; // X ... X
+ // fmov Vd,Rn DV_2I X00111100X100111 000000nnnnnddddd 1E27 0000 Vd,Rn
+ // (scalar, from general)
+ assert(isVectorRegister(dstReg) && isGeneralRegister(addrReg));
+ ins = INS_fmov;
+ fmt = IF_DV_2I;
+ code_t code = emitInsCode(ins, fmt);
+
+ code |= insEncodeReg_Vd(dstReg); // ddddd
+ code |= insEncodeReg_Rn(addrReg); // nnnnn
+ if (id->idOpSize() == EA_8BYTE)
+ {
+ code |= 0x80400000; // X ... X
+ }
+ dst += emitOutput_Instr(dst, code);
}
- dst += emitOutput_Instr(dst, code);
}
}
}
@@ -9933,12 +9980,6 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i)
/* For forward jumps, record the address of the distance value */
id->idjTemp.idjAddr = (distVal > 0) ? dst : NULL;
- if (emitJumpCrossHotColdBoundary(srcOffs, dstOffs))
- {
- assert(!id->idjShort);
- NYI_ARM64("Relocation Support for long address");
- }
-
assert(insOptsNone(id->idInsOpt()));
if (isJump)
@@ -9949,75 +9990,114 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i)
assert(!id->idjKeepLong);
assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false);
assert((fmt == IF_BI_0A) || (fmt == IF_BI_0B) || (fmt == IF_BI_1A) || (fmt == IF_BI_1B));
+ dst = emitOutputShortBranch(dst, ins, fmt, distVal, id);
}
else
{
- // Long conditional jump
- assert(fmt == IF_LARGEJMP);
- // This is a pseudo-instruction format representing a large conditional branch, to allow
- // us to get a greater branch target range than we can get by using a straightforward conditional
- // branch. It is encoded as a short conditional branch that branches around a long unconditional
- // branch.
- //
- // Conceptually, we have:
- //
- // b L_target
- //
- // The code we emit is:
- //
- // b L_not // 4 bytes. Note that we reverse the condition.
- // b L_target // 4 bytes
- // L_not:
- //
- // Note that we don't actually insert any blocks: we simply encode "b L_not" as a branch with
- // the correct offset. Note also that this works for both integer and floating-point conditions, because
- // the condition inversion takes ordered/unordered into account, preserving NaN behavior. For example,
- // "GT" (greater than) is inverted to "LE" (less than, equal, or unordered).
+ // Long conditional/unconditional jump
- instruction reverseIns;
- insFormat reverseFmt;
+ if (fmt == IF_LARGEJMP)
+ {
+ // This is a pseudo-instruction format representing a large conditional branch, to allow
+ // us to get a greater branch target range than we can get by using a straightforward conditional
+ // branch. It is encoded as a short conditional branch that branches around a long unconditional
+ // branch.
+ //
+ // Conceptually, we have:
+ //
+ // b L_target
+ //
+ // The code we emit is:
+ //
+ // b L_not // 4 bytes. Note that we reverse the condition.
+ // b L_target // 4 bytes
+ // L_not:
+ //
+ // Note that we don't actually insert any blocks: we simply encode "b L_not" as a branch with
+ // the correct offset. Note also that this works for both integer and floating-point conditions, because
+ // the condition inversion takes ordered/unordered into account, preserving NaN behavior. For example,
+ // "GT" (greater than) is inverted to "LE" (less than, equal, or unordered).
- switch (ins)
+ instruction reverseIns;
+ insFormat reverseFmt;
+
+ switch (ins)
+ {
+ case INS_cbz:
+ reverseIns = INS_cbnz;
+ reverseFmt = IF_BI_1A;
+ break;
+ case INS_cbnz:
+ reverseIns = INS_cbz;
+ reverseFmt = IF_BI_1A;
+ break;
+ case INS_tbz:
+ reverseIns = INS_tbnz;
+ reverseFmt = IF_BI_1B;
+ break;
+ case INS_tbnz:
+ reverseIns = INS_tbz;
+ reverseFmt = IF_BI_1B;
+ break;
+ default:
+ reverseIns = emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(ins)));
+ reverseFmt = IF_BI_0B;
+ }
+
+ dst = emitOutputShortBranch(dst,
+ reverseIns, // reverse the conditional instruction
+ reverseFmt, 8, /* 8 bytes from start of this large conditional
+ pseudo-instruction to L_not. */
+ id);
+
+ // Now, pretend we've got a normal unconditional branch, and fall through to the code to emit that.
+ ins = INS_b;
+ fmt = IF_BI_0A;
+
+ // The distVal was computed based on the beginning of the pseudo-instruction,
+ // So subtract the size of the conditional branch so that it is relative to the
+ // unconditional branch.
+ distVal -= 4;
+ }
+
+ assert(fmt == IF_BI_0A);
+ assert((distVal & 1) == 0);
+ code_t code = emitInsCode(ins, fmt);
+ const bool recordRelocation = emitComp->opts.compReloc && emitJumpCrossHotColdBoundary(srcOffs, dstOffs);
+
+ if (recordRelocation)
{
- case INS_cbz:
- reverseIns = INS_cbnz;
- reverseFmt = IF_BI_1A;
- break;
- case INS_cbnz:
- reverseIns = INS_cbz;
- reverseFmt = IF_BI_1A;
- break;
- case INS_tbz:
- reverseIns = INS_tbnz;
- reverseFmt = IF_BI_1B;
- break;
- case INS_tbnz:
- reverseIns = INS_tbz;
- reverseFmt = IF_BI_1B;
- break;
- default:
- reverseIns = emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(ins)));
- reverseFmt = IF_BI_0B;
+ // dst isn't an actual final target location, just some intermediate
+ // location. Thus we cannot make any guarantees about distVal (not
+ // even the direction/sign). Instead we don't encode any offset and
+ // rely on the relocation to do all the work
}
+ else
+ {
+ // Branch offset encodings are scaled by 4.
+ noway_assert((distVal & 3) == 0);
+ distVal >>= 2;
+ noway_assert(isValidSimm26(distVal));
- dst =
- emitOutputShortBranch(dst,
- reverseIns, // reverse the conditional instruction
- reverseFmt,
- 8, /* 8 bytes from start of this large conditional pseudo-instruction to L_not. */
- id);
+ // Insert offset into unconditional branch instruction
+ distVal &= 0x3FFFFFFLL;
+ code |= distVal;
+ }
- // Now, pretend we've got a normal unconditional branch, and fall through to the code to emit that.
- ins = INS_b;
- fmt = IF_BI_0A;
+ const unsigned instrSize = emitOutput_Instr(dst, code);
- // The distVal was computed based on the beginning of the pseudo-instruction,
- // So subtract the size of the conditional branch so that it is relative to the
- // unconditional branch.
- distVal -= 4;
- }
+ if (recordRelocation)
+ {
+ assert(id->idjKeepLong);
+ if (emitComp->info.compMatchedVM)
+ {
+ void* target = emitOffsetToPtr(dstOffs);
+ emitRecordRelocation((void*)dst, target, IMAGE_REL_ARM64_BRANCH26);
+ }
+ }
- dst = emitOutputShortBranch(dst, ins, fmt, distVal, id);
+ dst += instrSize;
+ }
}
else if (loadLabel)
{
@@ -10138,7 +10218,7 @@ BYTE* emitter::emitOutputShortConstant(
ssize_t loBits = (imm & 3);
noway_assert(loBits == 0);
- ssize_t distVal = imm >>= 2; // load offset encodings are scaled by 4.
+ ssize_t distVal = imm >> 2; // load offset encodings are scaled by 4.
noway_assert(isValidSimm19(distVal));
@@ -10206,6 +10286,33 @@ BYTE* emitter::emitOutputShortConstant(
return dst;
}
+
+/*****************************************************************************
+ *
+ * Output instructions to load a constant into a vector register.
+ */
+BYTE* emitter::emitOutputVectorConstant(
+ BYTE* dst, ssize_t imm, regNumber dstReg, regNumber addrReg, emitAttr opSize, emitAttr elemSize)
+{
+ // add addrReg, addrReg, page offs -- compute address = page addr + page offs.
+ code_t code = emitInsCode(INS_add, IF_DI_2A); // DI_2A X0010001shiiiiii iiiiiinnnnnddddd 1100 0000 imm(i12, sh)
+ code |= insEncodeDatasize(EA_8BYTE); // X - use EA_8BYTE, as we are calculating 64-bit address
+ code |= ((code_t)imm << 10); // iiiiiiiiiiii
+ code |= insEncodeReg_Rd(addrReg); // ddddd
+ code |= insEncodeReg_Rn(addrReg); // nnnnn
+ dst += emitOutput_Instr(dst, code);
+
+ // ld1 dstReg, addrReg -- load constant at address in addrReg into dstReg.
+ code = emitInsCode(INS_ld1, IF_LS_2D); // LS_2D .Q.............. ....ssnnnnnttttt Vt Rn
+ code |= insEncodeVectorsize(opSize); // Q
+ code |= insEncodeVLSElemsize(elemSize); // ss
+ code |= insEncodeReg_Rn(addrReg); // nnnnn
+ code |= insEncodeReg_Vt(dstReg); // ttttt
+ dst += emitOutput_Instr(dst, code);
+
+ return dst;
+}
+
/*****************************************************************************
*
* Output a call instruction.
@@ -12241,8 +12348,121 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz)
}
}
+/*****************************************************************************
+ *
+ * Handles printing of LARGEJMP pseudo-instruction.
+ */
+
+void emitter::emitDispLargeJmp(
+ instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig)
+{
+ // Note: don't touch the actual instrDesc. If we accidentally messed it up, it would create a very
+ // difficult-to-find bug.
+
+ instrDescJmp idJmp;
+ instrDescJmp* pidJmp = &idJmp;
+
+ memset(&idJmp, 0, sizeof(idJmp));
+
+ const instruction ins = id->idIns();
+ instruction reverseIns;
+ insFormat reverseFmt;
+
+ // Reverse the conditional instruction.
+ switch (ins)
+ {
+ case INS_cbz:
+ reverseIns = INS_cbnz;
+ reverseFmt = IF_BI_1A;
+ break;
+ case INS_cbnz:
+ reverseIns = INS_cbz;
+ reverseFmt = IF_BI_1A;
+ break;
+ case INS_tbz:
+ reverseIns = INS_tbnz;
+ reverseFmt = IF_BI_1B;
+ break;
+ case INS_tbnz:
+ reverseIns = INS_tbz;
+ reverseFmt = IF_BI_1B;
+ break;
+ default:
+ reverseIns = emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(ins)));
+ reverseFmt = IF_BI_0B;
+ }
+
+ pidJmp->idIns(reverseIns);
+ pidJmp->idInsFmt(reverseFmt);
+ pidJmp->idOpSize(id->idOpSize());
+ pidJmp->idAddr()->iiaSetInstrCount(1);
+ pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // Share the idDebugOnlyInfo() field.
+
+ const size_t bcondSizeOrZero = (pCode == NULL) ? 0 : 4; // Branch is 4 bytes.
+ emitDispInsHelp(pidJmp, false, doffs, asmfm, offset, pCode, bcondSizeOrZero,
+ NULL /* force display of pc-relative branch */);
+
+ pCode += bcondSizeOrZero;
+ offset += 4;
+
+ // Next, display the unconditional branch.
+
+ // Reset the local instrDesc.
+ memset(&idJmp, 0, sizeof(idJmp));
+
+ pidJmp->idIns(INS_b);
+ pidJmp->idInsFmt(IF_LARGEJMP);
+
+ if (id->idIsBound())
+ {
+ pidJmp->idSetIsBound();
+ pidJmp->idAddr()->iiaIGlabel = id->idAddr()->iiaIGlabel;
+ }
+ else
+ {
+ pidJmp->idAddr()->iiaBBlabel = id->idAddr()->iiaBBlabel;
+ }
+
+ pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // Share the idDebugOnlyInfo() field.
+
+ const size_t brSizeOrZero = (pCode == NULL) ? 0 : 4; // Unconditional branch is 4 bytes.
+ emitDispInsHelp(pidJmp, isNew, doffs, asmfm, offset, pCode, brSizeOrZero, ig);
+}
+
+/*****************************************************************************
+ *
+ * Wrapper for emitter::emitDispInsHelp() that handles special large jump
+ * pseudo-instruction.
+ */
+
+void emitter::emitDispIns(
+ instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig)
+{
+ // Special case: IF_LARGEJMP
+
+ if ((id->idInsFmt() == IF_LARGEJMP) && id->idIsBound())
+ {
+ // This is a pseudo-instruction format representing a large conditional branch. See the comment
+ // in emitter::emitOutputLJ() for the full description.
+ //
+ // For this pseudo-instruction, we will actually generate:
+ //
+ // b L_not // 4 bytes. Note that we reverse the condition.
+ // b L_target // 4 bytes.
+ // L_not:
+ //
+ // These instructions don't exist in the actual instruction stream, so we need to fake them
+ // up to display them.
+ emitDispLargeJmp(id, isNew, doffs, asmfm, offset, pCode, sz, ig);
+ }
+ else
+ {
+ emitDispInsHelp(id, isNew, doffs, asmfm, offset, pCode, sz, ig);
+ }
+}
+
//--------------------------------------------------------------------
-// emitDispIns: Dump the given instruction to jitstdout.
+// emitDispInsHelp: Dump the given instruction to jitstdout.
//
// Arguments:
// id - The instruction
@@ -12257,7 +12477,7 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz)
// sz - The size of the instruction, used to display the encoded bytes.
// ig - The instruction group containing the instruction.
//
-void emitter::emitDispIns(
+void emitter::emitDispInsHelp(
instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig)
{
if (EMITVERBOSE)
@@ -12269,7 +12489,9 @@ void emitter::emitDispIns(
}
if (pCode == NULL)
+ {
sz = 0;
+ }
if (!isNew && !asmfm && sz)
{
@@ -12379,23 +12601,34 @@ void emitter::emitDispIns(
break;
case IF_BI_1A: // BI_1A ......iiiiiiiiii iiiiiiiiiiittttt Rt simm19:00
+ case IF_BI_1B: // BI_1B B.......bbbbbiii iiiiiiiiiiittttt Rt imm6, simm14:00
+ {
assert(insOptsNone(id->idInsOpt()));
emitDispReg(id->idReg1(), size, true);
- if (id->idIsBound())
+
+ if (fmt == IF_BI_1B)
{
- emitPrintLabel(id->idAddr()->iiaIGlabel);
+ emitDispImm(emitGetInsSC(id), true);
}
- else
+
+ if (id->idAddr()->iiaHasInstrCount())
{
- printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum);
- }
- break;
+ int instrCount = id->idAddr()->iiaGetInstrCount();
- case IF_BI_1B: // BI_1B B.......bbbbbiii iiiiiiiiiiittttt Rt imm6, simm14:00
- assert(insOptsNone(id->idInsOpt()));
- emitDispReg(id->idReg1(), size, true);
- emitDispImm(emitGetInsSC(id), true);
- if (id->idIsBound())
+ if (ig == nullptr)
+ {
+ printf("pc%s%d instructions", (instrCount >= 0) ? "+" : "", instrCount);
+ }
+ else
+ {
+ unsigned insNum = emitFindInsNum(ig, id);
+ UNATIVE_OFFSET srcOffs = ig->igOffs + emitFindOffset(ig, insNum + 1);
+ UNATIVE_OFFSET dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount);
+ ssize_t relOffs = (ssize_t)(emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs));
+ printf("pc%s%d (%d instructions)", (relOffs >= 0) ? "+" : "", relOffs, instrCount);
+ }
+ }
+ else if (id->idIsBound())
{
emitPrintLabel(id->idAddr()->iiaIGlabel);
}
@@ -12403,7 +12636,8 @@ void emitter::emitDispIns(
{
printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum);
}
- break;
+ }
+ break;
case IF_BR_1A: // BR_1A ................ ......nnnnn..... Rn
assert(insOptsNone(id->idInsOpt()));
@@ -12487,7 +12721,7 @@ void emitter::emitDispIns(
}
else
{
- emitDispCommentForHandle(id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
+ emitDispCommentForHandle(id->idDebugOnlyInfo()->idMemCookie, 0, id->idDebugOnlyInfo()->idFlags);
}
break;
@@ -12623,6 +12857,7 @@ void emitter::emitDispIns(
case IF_DI_1A: // DI_1A X.......shiiiiii iiiiiinnnnn..... Rn imm(i12,sh)
emitDispReg(id->idReg1(), size, true);
emitDispImmOptsLSL12(emitGetInsSC(id), id->idInsOpt());
+ emitDispCommentForHandle(0, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
break;
case IF_DI_1B: // DI_1B X........hwiiiii iiiiiiiiiiiddddd Rd imm(i16,hw)
@@ -12641,18 +12876,21 @@ void emitter::emitDispIns(
emitDispImm(hwi.immHW * 16, false);
}
}
+ emitDispCommentForHandle(0, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
break;
case IF_DI_1C: // DI_1C X........Nrrrrrr ssssssnnnnn..... Rn imm(N,r,s)
emitDispReg(id->idReg1(), size, true);
bmi.immNRS = (unsigned)emitGetInsSC(id);
emitDispImm(emitDecodeBitMaskImm(bmi, size), false);
+ emitDispCommentForHandle(0, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
break;
case IF_DI_1D: // DI_1D X........Nrrrrrr ssssss.....ddddd Rd imm(N,r,s)
emitDispReg(encodingZRtoSP(id->idReg1()), size, true);
bmi.immNRS = (unsigned)emitGetInsSC(id);
emitDispImm(emitDecodeBitMaskImm(bmi, size), false);
+ emitDispCommentForHandle(0, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
break;
case IF_DI_2A: // DI_2A X.......shiiiiii iiiiiinnnnnddddd Rd Rn imm(i12,sh)
diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h
index 8970c15b3c090c..4068fe3c254085 100644
--- a/src/coreclr/jit/emitarm64.h
+++ b/src/coreclr/jit/emitarm64.h
@@ -23,6 +23,10 @@ static bool strictArmAsm;
const char* emitVectorRegName(regNumber reg);
+void emitDispInsHelp(
+ instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig);
+void emitDispLargeJmp(
+ instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig);
void emitDispInst(instruction ins);
void emitDispImm(ssize_t imm, bool addComma, bool alwaysHex = false);
void emitDispFloatZero();
@@ -726,7 +730,8 @@ void emitIns_R_I(instruction ins,
emitAttr attr,
regNumber reg,
ssize_t imm,
- insOpts opt = INS_OPTS_NONE DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
+ insOpts opt = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0)
+ DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
void emitIns_R_F(instruction ins, emitAttr attr, regNumber reg, double immDbl, insOpts opt = INS_OPTS_NONE);
@@ -740,8 +745,13 @@ void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2,
emitIns_R_R(ins, attr, reg1, reg2);
}
-void emitIns_R_I_I(
- instruction ins, emitAttr attr, regNumber reg1, ssize_t imm1, ssize_t imm2, insOpts opt = INS_OPTS_NONE);
+void emitIns_R_I_I(instruction ins,
+ emitAttr attr,
+ regNumber reg1,
+ ssize_t imm1,
+ ssize_t imm2,
+ insOpts opt = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0)
+ DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
void emitIns_R_R_I(
instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insOpts opt = INS_OPTS_NONE);
@@ -867,6 +877,8 @@ BYTE* emitOutputShortBranch(BYTE* dst, instruction ins, insFormat fmt, ssize_t d
BYTE* emitOutputShortAddress(BYTE* dst, instruction ins, insFormat fmt, ssize_t distVal, regNumber reg);
BYTE* emitOutputShortConstant(
BYTE* dst, instruction ins, insFormat fmt, ssize_t distVal, regNumber reg, emitAttr opSize);
+BYTE* emitOutputVectorConstant(
+ BYTE* dst, ssize_t distVal, regNumber dstReg, regNumber addrReg, emitAttr opSize, emitAttr elemSize);
/*****************************************************************************
*
diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp
index 8e3802123dd5ac..57987606a903c7 100644
--- a/src/coreclr/jit/emitloongarch64.cpp
+++ b/src/coreclr/jit/emitloongarch64.cpp
@@ -3912,7 +3912,7 @@ static const char* const RegNames[] =
void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id)
{
- const BYTE* insAdr = addr;
+ const BYTE* insAdr = addr - writeableOffset;
const char* const CFregName[] = {"fcc0", "fcc1", "fcc2", "fcc3", "fcc4", "fcc5", "fcc6", "fcc7"};
unsigned int opcode = (code >> 26) & 0x3f;
diff --git a/src/coreclr/jit/emitloongarch64.h b/src/coreclr/jit/emitloongarch64.h
index d7e7cc5450acbf..da24986527182c 100644
--- a/src/coreclr/jit/emitloongarch64.h
+++ b/src/coreclr/jit/emitloongarch64.h
@@ -133,6 +133,12 @@ inline static bool isFloatReg(regNumber reg)
return (reg >= REG_FP_FIRST && reg <= REG_FP_LAST);
}
+/************************************************************************/
+/* Output target-independent instructions */
+/************************************************************************/
+
+void emitIns_J(instruction ins, BasicBlock* dst, int instrCount = 0);
+
/************************************************************************/
/* The public entry points to output instructions */
/************************************************************************/
diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp
index 707c8fc25f7c38..bd5b2d0defe6a7 100644
--- a/src/coreclr/jit/emitxarch.cpp
+++ b/src/coreclr/jit/emitxarch.cpp
@@ -3219,6 +3219,11 @@ void emitter::emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt,
id->idAddr()->iiaFieldHnd = fldHnd;
id->idInsFmt(emitMapFmtForIns(emitMapFmtAtoM(fmt), ins));
+
+#ifdef DEBUG
+ id->idDebugOnlyInfo()->idFlags = GTF_ICON_STATIC_HDL;
+ id->idDebugOnlyInfo()->idMemCookie = reinterpret_cast(fldHnd);
+#endif
}
else if ((memBase != nullptr) && memBase->IsCnsIntOrI() && memBase->isContained())
{
@@ -3279,6 +3284,14 @@ void emitter::emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt,
// disp must have already been set in the instrDesc constructor.
assert(emitGetInsAmdAny(id) == indir->Offset()); // make sure "disp" is stored properly
}
+
+#ifdef DEBUG
+ if ((memBase != nullptr) && memBase->IsIconHandle() && memBase->isContained())
+ {
+ id->idDebugOnlyInfo()->idFlags = memBase->gtFlags;
+ id->idDebugOnlyInfo()->idMemCookie = memBase->AsIntCon()->gtTargetHandle;
+ }
+#endif
}
// Takes care of storing all incoming register parameters
@@ -4126,7 +4139,10 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg)
* Add an instruction referencing a register and a constant.
*/
-void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val DEBUGARG(GenTreeFlags gtFlags))
+void emitter::emitIns_R_I(instruction ins,
+ emitAttr attr,
+ regNumber reg,
+ ssize_t val DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags))
{
emitAttr size = EA_SIZE(attr);
@@ -4259,7 +4275,11 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t
id->idInsFmt(fmt);
id->idReg1(reg);
id->idCodeSize(sz);
- INDEBUG(id->idDebugOnlyInfo()->idFlags = gtFlags);
+
+#ifdef DEBUG
+ id->idDebugOnlyInfo()->idFlags = gtFlags;
+ id->idDebugOnlyInfo()->idMemCookie = targetHandle;
+#endif
dispIns(id);
emitCurIGsize += sz;
@@ -9118,7 +9138,7 @@ void emitter::emitDispIns(
{ // (val < 0)
printf("-0x%IX", -val);
}
- emitDispCommentForHandle(srcVal, id->idDebugOnlyInfo()->idFlags);
+ emitDispCommentForHandle(srcVal, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags);
}
break;
@@ -9189,6 +9209,9 @@ void emitter::emitDispIns(
}
printf("%s, %s", emitRegName(id->idReg1(), attr), sstr);
emitDispAddrMode(id);
+
+ emitDispCommentForHandle(emitGetInsAmdAny(id), id->idDebugOnlyInfo()->idMemCookie,
+ id->idDebugOnlyInfo()->idFlags);
break;
case IF_RRW_ARD_CNS:
diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h
index 754632bb1c0896..c744e40dbd41ea 100644
--- a/src/coreclr/jit/emitxarch.h
+++ b/src/coreclr/jit/emitxarch.h
@@ -330,7 +330,10 @@ void emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int
void emitIns_A(instruction ins, emitAttr attr, GenTreeIndir* indir);
-void emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
+void emitIns_R_I(instruction ins,
+ emitAttr attr,
+ regNumber reg,
+ ssize_t val DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
void emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip);
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index 148dc02af96493..541bdc27e7c962 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -186,7 +186,7 @@ void Compiler::fgInit()
fgPgoInlineeNoPgo = 0;
fgPgoInlineeNoPgoSingleBlock = 0;
fgCountInstrumentor = nullptr;
- fgClassInstrumentor = nullptr;
+ fgHistogramInstrumentor = nullptr;
fgPredListSortVector = nullptr;
}
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index fe0746aa26d72e..d59301991aaa61 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -274,7 +274,7 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call)
return;
}
- InlineResult inlineResult(this, call, nullptr, "fgNoteNonInlineCandidate");
+ InlineResult inlineResult(this, call, nullptr, "fgNoteNonInlineCandidate", false);
InlineObservation currentObservation = InlineObservation::CALLSITE_NOT_CANDIDATE;
// Try and recover the reason left behind when the jit decided
@@ -288,7 +288,6 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call)
// Propagate the prior failure observation to this result.
inlineResult.NotePriorFailure(currentObservation);
- inlineResult.SetReported();
if (call->gtCallType == CT_USER_FUNC)
{
diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp
index d0d4ae647ec989..360633d901e939 100644
--- a/src/coreclr/jit/fgopt.cpp
+++ b/src/coreclr/jit/fgopt.cpp
@@ -3221,8 +3221,10 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
if (verbose)
{
printf("\nConverting a switch (" FMT_BB ") with only one significant clause besides a default target to a "
- "conditional branch\n",
+ "conditional branch. Before:\n",
block->bbNum);
+
+ gtDispTree(switchTree);
}
#endif // DEBUG
@@ -3247,6 +3249,9 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
block->bbJumpDest = block->bbJumpSwt->bbsDstTab[0];
block->bbJumpKind = BBJ_COND;
+ JITDUMP("After:\n");
+ DISPNODE(switchTree);
+
return true;
}
return returnvalue;
diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp
index bfd7000486c8bd..4eb555a65ca94e 100644
--- a/src/coreclr/jit/fgprofile.cpp
+++ b/src/coreclr/jit/fgprofile.cpp
@@ -1425,11 +1425,11 @@ void EfficientEdgeCountInstrumentor::Instrument(BasicBlock* block, Schema& schem
}
//------------------------------------------------------------------------
-// ClassProbeVisitor: invoke functor on each virtual call or cast-related
+// HandleHistogramProbeVisitor: invoke functor on each virtual call or cast-related
// helper calls in a tree
//
template
-class ClassProbeVisitor final : public GenTreeVisitor>
+class HandleHistogramProbeVisitor final : public GenTreeVisitor>
{
public:
enum
@@ -1440,26 +1440,17 @@ class ClassProbeVisitor final : public GenTreeVisitor(compiler), m_functor(functor), m_compiler(compiler)
+ HandleHistogramProbeVisitor(Compiler* compiler, TFunctor& functor)
+ : GenTreeVisitor(compiler), m_functor(functor), m_compiler(compiler)
{
}
Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
{
GenTree* const node = *use;
- if (node->IsCall() && (node->AsCall()->gtClassProfileCandidateInfo != nullptr))
+ if (node->IsCall() && (m_compiler->compClassifyGDVProbeType(node->AsCall()) != Compiler::GDVProbeType::None))
{
- GenTreeCall* const call = node->AsCall();
- if (call->IsVirtual() && (call->gtCallType != CT_INDIRECT))
- {
- // virtual call
- m_functor(m_compiler, call);
- }
- else if (m_compiler->impIsCastHelperEligibleForClassProbe(call))
- {
- // isinst/cast helper
- m_functor(m_compiler, call);
- }
+ assert(node->AsCall()->gtHandleHistogramProfileCandidateInfo != nullptr);
+ m_functor(m_compiler, node->AsCall());
}
return Compiler::WALK_CONTINUE;
@@ -1467,44 +1458,65 @@ class ClassProbeVisitor final : public GenTreeVisitorcompClassifyGDVProbeType(call);
+
+ if ((probeType == Compiler::GDVProbeType::ClassProfile) ||
+ (probeType == Compiler::GDVProbeType::MethodAndClassProfile))
+ {
+ CreateHistogramSchemaEntries(compiler, call, true /* isTypeHistogram */);
+ }
+
+ if ((probeType == Compiler::GDVProbeType::MethodProfile) ||
+ (probeType == Compiler::GDVProbeType::MethodAndClassProfile))
+ {
+ CreateHistogramSchemaEntries(compiler, call, false /* isTypeHistogram */);
+ }
+ }
+
+ void CreateHistogramSchemaEntries(Compiler* compiler, GenTreeCall* call, bool isTypeHistogram)
+ {
+ ICorJitInfo::PgoInstrumentationSchema schemaElem = {};
+ schemaElem.Count = 1;
+ schemaElem.Other = isTypeHistogram ? ICorJitInfo::HandleHistogram32::CLASS_FLAG : 0;
if (call->IsVirtualStub())
{
- schemaElem.Other |= ICorJitInfo::ClassProfile32::INTERFACE_FLAG;
+ schemaElem.Other |= ICorJitInfo::HandleHistogram32::INTERFACE_FLAG;
}
- else
+ else if (call->IsDelegateInvoke())
{
- assert(call->IsVirtualVtable() || compiler->impIsCastHelperEligibleForClassProbe(call));
+ schemaElem.Other |= ICorJitInfo::HandleHistogram32::DELEGATE_FLAG;
}
schemaElem.InstrumentationKind = JitConfig.JitCollect64BitCounts()
? ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount
: ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount;
- schemaElem.ILOffset = (int32_t)call->gtClassProfileCandidateInfo->ilOffset;
+ schemaElem.ILOffset = (int32_t)call->gtHandleHistogramProfileCandidateInfo->ilOffset;
schemaElem.Offset = 0;
m_schema.push_back(schemaElem);
+ m_schemaCount++;
+
// Re-using ILOffset and Other fields from schema item for TypeHandleHistogramCount
- schemaElem.InstrumentationKind = ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes;
- schemaElem.Count = ICorJitInfo::ClassProfile32::SIZE;
+ schemaElem.InstrumentationKind = isTypeHistogram ? ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes
+ : ICorJitInfo::PgoInstrumentationKind::HandleHistogramMethods;
+ schemaElem.Count = ICorJitInfo::HandleHistogram32::SIZE;
m_schema.push_back(schemaElem);
m_schemaCount++;
@@ -1512,9 +1524,9 @@ class BuildClassProbeSchemaGen
};
//------------------------------------------------------------------------
-// ClassProbeInserter: functor that adds class probe instrumentation
+// HandleHistogramProbeInserter: functor that adds class/method probe instrumentation
//
-class ClassProbeInserter
+class HandleHistogramProbeInserter
{
Schema& m_schema;
uint8_t* m_profileMemory;
@@ -1522,7 +1534,7 @@ class ClassProbeInserter
unsigned& m_instrCount;
public:
- ClassProbeInserter(Schema& schema, uint8_t* profileMemory, int* pCurrentSchemaIndex, unsigned& instrCount)
+ HandleHistogramProbeInserter(Schema& schema, uint8_t* profileMemory, int* pCurrentSchemaIndex, unsigned& instrCount)
: m_schema(schema)
, m_profileMemory(profileMemory)
, m_currentSchemaIndex(pCurrentSchemaIndex)
@@ -1533,10 +1545,11 @@ class ClassProbeInserter
void operator()(Compiler* compiler, GenTreeCall* call)
{
JITDUMP("Found call [%06u] with probe index %d and ilOffset 0x%X\n", compiler->dspTreeID(call),
- call->gtClassProfileCandidateInfo->probeIndex, call->gtClassProfileCandidateInfo->ilOffset);
+ call->gtHandleHistogramProfileCandidateInfo->probeIndex,
+ call->gtHandleHistogramProfileCandidateInfo->ilOffset);
// We transform the call from (CALLVIRT obj, ... args ...) to
- // to
+ //
// (CALLVIRT
// (COMMA
// (ASG tmp, obj)
@@ -1546,19 +1559,25 @@ class ClassProbeInserter
// ... args ...)
//
- // Sanity check that we're looking at the right schema entry
- //
- assert(m_schema[*m_currentSchemaIndex].ILOffset == (int32_t)call->gtClassProfileCandidateInfo->ilOffset);
- bool is32 = m_schema[*m_currentSchemaIndex].InstrumentationKind ==
- ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount;
- bool is64 = m_schema[*m_currentSchemaIndex].InstrumentationKind ==
- ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount;
- assert(is32 || is64);
-
- // Figure out where the table is located.
- //
- uint8_t* classProfile = m_schema[*m_currentSchemaIndex].Offset + m_profileMemory;
- *m_currentSchemaIndex += 2; // There are 2 schema entries per class probe
+ // Read histograms
+ void* typeHistogram = nullptr;
+ void* methodHistogram = nullptr;
+
+ bool is32;
+ ReadHistogramAndAdvance(call->gtHandleHistogramProfileCandidateInfo->ilOffset, &typeHistogram, &methodHistogram,
+ &is32);
+ bool secondIs32;
+ ReadHistogramAndAdvance(call->gtHandleHistogramProfileCandidateInfo->ilOffset, &typeHistogram, &methodHistogram,
+ &secondIs32);
+
+ assert(((typeHistogram != nullptr) || (methodHistogram != nullptr)) &&
+ "Expected at least one handle histogram when inserting probes");
+
+ if ((typeHistogram != nullptr) && (methodHistogram != nullptr))
+ {
+ // We expect both histograms to be 32-bit or 64-bit, not a mix.
+ assert(is32 == secondIs32);
+ }
assert(!call->gtArgs.AreArgsComplete());
CallArg* objUse = nullptr;
@@ -1576,20 +1595,57 @@ class ClassProbeInserter
// Grab a temp to hold the 'this' object as it will be used three times
//
- unsigned const tmpNum = compiler->lvaGrabTemp(true DEBUGARG("class profile tmp"));
+ unsigned const tmpNum = compiler->lvaGrabTemp(true DEBUGARG("handle histogram profile tmp"));
compiler->lvaTable[tmpNum].lvType = TYP_REF;
+ GenTree* helperCallNode = nullptr;
+
+ if (typeHistogram != nullptr)
+ {
+ GenTree* const tmpNode = compiler->gtNewLclvNode(tmpNum, TYP_REF);
+ GenTree* const classProfileNode = compiler->gtNewIconNode((ssize_t)typeHistogram, TYP_I_IMPL);
+ helperCallNode =
+ compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_CLASSPROFILE32 : CORINFO_HELP_CLASSPROFILE64,
+ TYP_VOID, tmpNode, classProfileNode);
+ }
+
+ if (methodHistogram != nullptr)
+ {
+ GenTree* const tmpNode = compiler->gtNewLclvNode(tmpNum, TYP_REF);
+ GenTree* const methodProfileNode = compiler->gtNewIconNode((ssize_t)methodHistogram, TYP_I_IMPL);
+
+ GenTree* methodProfileCallNode;
+ if (call->IsDelegateInvoke())
+ {
+ methodProfileCallNode = compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_DELEGATEPROFILE32
+ : CORINFO_HELP_DELEGATEPROFILE64,
+ TYP_VOID, tmpNode, methodProfileNode);
+ }
+ else
+ {
+ assert(call->IsVirtualVtable());
+ GenTree* const baseMethodNode = compiler->gtNewIconEmbMethHndNode(call->gtCallMethHnd);
+ methodProfileCallNode =
+ compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_VTABLEPROFILE32 : CORINFO_HELP_VTABLEPROFILE64,
+ TYP_VOID, tmpNode, baseMethodNode, methodProfileNode);
+ }
+
+ if (helperCallNode == nullptr)
+ {
+ helperCallNode = methodProfileCallNode;
+ }
+ else
+ {
+ helperCallNode = compiler->gtNewOperNode(GT_COMMA, TYP_REF, helperCallNode, methodProfileCallNode);
+ }
+ }
+
// Generate the IR...
//
- GenTree* const classProfileNode = compiler->gtNewIconNode((ssize_t)classProfile, TYP_I_IMPL);
- GenTree* const tmpNode = compiler->gtNewLclvNode(tmpNum, TYP_REF);
- GenTreeCall* const helperCallNode =
- compiler->gtNewHelperCallNode(is32 ? CORINFO_HELP_CLASSPROFILE32 : CORINFO_HELP_CLASSPROFILE64, TYP_VOID,
- tmpNode, classProfileNode);
GenTree* const tmpNode2 = compiler->gtNewLclvNode(tmpNum, TYP_REF);
GenTree* const callCommaNode = compiler->gtNewOperNode(GT_COMMA, TYP_REF, helperCallNode, tmpNode2);
GenTree* const tmpNode3 = compiler->gtNewLclvNode(tmpNum, TYP_REF);
- GenTree* const asgNode = compiler->gtNewOperNode(GT_ASG, TYP_REF, tmpNode3, objUse->GetEarlyNode());
+ GenTree* const asgNode = compiler->gtNewOperNode(GT_ASG, TYP_REF, tmpNode3, objUse->GetNode());
GenTree* const asgCommaNode = compiler->gtNewOperNode(GT_COMMA, TYP_REF, asgNode, callCommaNode);
// Update the call
@@ -1601,16 +1657,78 @@ class ClassProbeInserter
m_instrCount++;
}
+
+private:
+ void ReadHistogramAndAdvance(IL_OFFSET ilOffset, void** typeHistogram, void** methodHistogram, bool* histogramIs32)
+ {
+ if (*m_currentSchemaIndex >= (int)m_schema.size())
+ {
+ return;
+ }
+
+ ICorJitInfo::PgoInstrumentationSchema& countEntry = m_schema[*m_currentSchemaIndex];
+
+ bool is32 = countEntry.InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount;
+ bool is64 = countEntry.InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount;
+ if (!is32 && !is64)
+ {
+ return;
+ }
+
+ if (countEntry.ILOffset != static_cast(ilOffset))
+ {
+ return;
+ }
+
+ assert(*m_currentSchemaIndex + 2 <= (int)m_schema.size());
+ ICorJitInfo::PgoInstrumentationSchema& tableEntry = m_schema[*m_currentSchemaIndex + 1];
+ assert((tableEntry.InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes) ||
+ (tableEntry.InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramMethods));
+
+ void** outHistogram;
+ if (tableEntry.InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes)
+ {
+ assert(*typeHistogram == nullptr);
+ outHistogram = typeHistogram;
+ }
+ else
+ {
+ assert(*methodHistogram == nullptr);
+ outHistogram = methodHistogram;
+ }
+
+ *outHistogram = &m_profileMemory[countEntry.Offset];
+ *histogramIs32 = is32;
+
+#ifdef DEBUG
+ if (is32)
+ {
+ ICorJitInfo::HandleHistogram32* h32 =
+ reinterpret_cast(&m_profileMemory[countEntry.Offset]);
+ assert(reinterpret_cast(&h32->Count) == &m_profileMemory[countEntry.Offset]);
+ assert(reinterpret_cast(h32->HandleTable) == &m_profileMemory[tableEntry.Offset]);
+ }
+ else
+ {
+ ICorJitInfo::HandleHistogram64* h64 =
+ reinterpret_cast(&m_profileMemory[countEntry.Offset]);
+ assert(reinterpret_cast(&h64->Count) == &m_profileMemory[countEntry.Offset]);
+ assert(reinterpret_cast(h64->HandleTable) == &m_profileMemory[tableEntry.Offset]);
+ }
+#endif
+
+ *m_currentSchemaIndex += 2;
+ }
};
//------------------------------------------------------------------------
-// ClassProbeInstrumentor: instrumentor that adds a class probe to each
+// HandleHistogramProbeInstrumentor: instrumentor that adds a class probe to each
// virtual call in the basic block
//
-class ClassProbeInstrumentor : public Instrumentor
+class HandleHistogramProbeInstrumentor : public Instrumentor
{
public:
- ClassProbeInstrumentor(Compiler* comp) : Instrumentor(comp)
+ HandleHistogramProbeInstrumentor(Compiler* comp) : Instrumentor(comp)
{
}
bool ShouldProcess(BasicBlock* block) override
@@ -1623,13 +1741,13 @@ class ClassProbeInstrumentor : public Instrumentor
};
//------------------------------------------------------------------------
-// ClassProbeInstrumentor::Prepare: prepare for class instrumentation
+// HandleHistogramProbeInstrumentor::Prepare: prepare for class instrumentation
//
// Arguments:
// preImport - true if this is the prepare call that happens before
// importation
//
-void ClassProbeInstrumentor::Prepare(bool isPreImport)
+void HandleHistogramProbeInstrumentor::Prepare(bool isPreImport)
{
if (isPreImport)
{
@@ -1641,33 +1759,33 @@ void ClassProbeInstrumentor::Prepare(bool isPreImport)
//
for (BasicBlock* const block : m_comp->Blocks())
{
- block->bbClassSchemaIndex = -1;
+ block->bbHistogramSchemaIndex = -1;
}
#endif
}
//------------------------------------------------------------------------
-// ClassProbeInstrumentor::BuildSchemaElements: create schema elements for a class probe
+// HandleHistogramProbeInstrumentor::BuildSchemaElements: create schema elements for a class probe
//
// Arguments:
// block -- block to instrument
// schema -- schema that we're building
//
-void ClassProbeInstrumentor::BuildSchemaElements(BasicBlock* block, Schema& schema)
+void HandleHistogramProbeInstrumentor::BuildSchemaElements(BasicBlock* block, Schema& schema)
{
- if ((block->bbFlags & BBF_HAS_CLASS_PROFILE) == 0)
+ if ((block->bbFlags & BBF_HAS_HISTOGRAM_PROFILE) == 0)
{
return;
}
// Remember the schema index for this block.
//
- block->bbClassSchemaIndex = (int)schema.size();
+ block->bbHistogramSchemaIndex = (int)schema.size();
// Scan the statements and identify the class probes
//
- BuildClassProbeSchemaGen schemaGen(schema, m_schemaCount);
- ClassProbeVisitor visitor(m_comp, schemaGen);
+ BuildHandleHistogramProbeSchemaGen schemaGen(schema, m_schemaCount);
+ HandleHistogramProbeVisitor visitor(m_comp, schemaGen);
for (Statement* const stmt : block->Statements())
{
visitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
@@ -1675,16 +1793,16 @@ void ClassProbeInstrumentor::BuildSchemaElements(BasicBlock* block, Schema& sche
}
//------------------------------------------------------------------------
-// ClassProbeInstrumentor::Instrument: add class probes to block
+// HandleHistogramProbeInstrumentor::Instrument: add class probes to block
//
// Arguments:
// block -- block of interest
// schema -- instrumentation schema
// profileMemory -- profile data slab
//
-void ClassProbeInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
+void HandleHistogramProbeInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
{
- if ((block->bbFlags & BBF_HAS_CLASS_PROFILE) == 0)
+ if ((block->bbFlags & BBF_HAS_HISTOGRAM_PROFILE) == 0)
{
return;
}
@@ -1696,11 +1814,11 @@ void ClassProbeInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8
// Scan the statements and add class probes
//
- int classSchemaIndex = block->bbClassSchemaIndex;
- assert((classSchemaIndex >= 0) && (classSchemaIndex < (int)schema.size()));
+ int histogramSchemaIndex = block->bbHistogramSchemaIndex;
+ assert((histogramSchemaIndex >= 0) && (histogramSchemaIndex < (int)schema.size()));
- ClassProbeInserter insertProbes(schema, profileMemory, &classSchemaIndex, m_instrCount);
- ClassProbeVisitor visitor(m_comp, insertProbes);
+ HandleHistogramProbeInserter insertProbes(schema, profileMemory, &histogramSchemaIndex, m_instrCount);
+ HandleHistogramProbeVisitor visitor(m_comp, insertProbes);
for (Statement* const stmt : block->Statements())
{
visitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
@@ -1789,24 +1907,25 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod()
// Enable class profiling by default, when jitting.
// Todo: we may also want this on by default for prejitting.
//
- const bool useClassProfiles = (JitConfig.JitClassProfiling() > 0) && !prejit;
- if (useClassProfiles)
+ const bool useClassProfiles = (JitConfig.JitClassProfiling() > 0);
+ const bool useDelegateProfiles = (JitConfig.JitDelegateProfiling() > 0);
+ const bool useVTableProfiles = (JitConfig.JitVTableProfiling() > 0);
+ if (!prejit && (useClassProfiles || useDelegateProfiles || useVTableProfiles))
{
- fgClassInstrumentor = new (this, CMK_Pgo) ClassProbeInstrumentor(this);
+ fgHistogramInstrumentor = new (this, CMK_Pgo) HandleHistogramProbeInstrumentor(this);
}
else
{
- JITDUMP("Not doing class profiling, because %s\n",
- (JitConfig.JitClassProfiling() > 0) ? "class profiles disabled" : "prejit");
+ JITDUMP("Not doing class/method profiling, because %s\n", prejit ? "prejit" : "class/method profiles disabled");
- fgClassInstrumentor = new (this, CMK_Pgo) NonInstrumentor(this);
+ fgHistogramInstrumentor = new (this, CMK_Pgo) NonInstrumentor(this);
}
// Make pre-import preparations.
//
const bool isPreImport = true;
fgCountInstrumentor->Prepare(isPreImport);
- fgClassInstrumentor->Prepare(isPreImport);
+ fgHistogramInstrumentor->Prepare(isPreImport);
return PhaseStatus::MODIFIED_NOTHING;
}
@@ -1835,7 +1954,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
//
const bool isPreImport = false;
fgCountInstrumentor->Prepare(isPreImport);
- fgClassInstrumentor->Prepare(isPreImport);
+ fgHistogramInstrumentor->Prepare(isPreImport);
// Walk the flow graph to build up the instrumentation schema.
//
@@ -1847,27 +1966,12 @@ PhaseStatus Compiler::fgInstrumentMethod()
fgCountInstrumentor->BuildSchemaElements(block, schema);
}
- if (fgClassInstrumentor->ShouldProcess(block))
+ if (fgHistogramInstrumentor->ShouldProcess(block))
{
- fgClassInstrumentor->BuildSchemaElements(block, schema);
+ fgHistogramInstrumentor->BuildSchemaElements(block, schema);
}
}
- // Verify we created schema for the calls needing class probes.
- // (we counted those when importing)
- //
- // This is not true when we do partial compilation; it can/will erase class probes,
- // and there's no easy way to figure out how many should be left.
- //
- if (doesMethodHavePartialCompilationPatchpoints())
- {
- assert(fgClassInstrumentor->SchemaCount() <= info.compClassProbeCount);
- }
- else
- {
- assert(fgClassInstrumentor->SchemaCount() == info.compClassProbeCount);
- }
-
// Optionally, when jitting, if there were no class probes and only one count probe,
// suppress instrumentation.
//
@@ -1887,7 +1991,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
minimalProbeMode = (JitConfig.JitMinimalJitProfiling() > 0);
}
- if (minimalProbeMode && (fgCountInstrumentor->SchemaCount() == 1) && (fgClassInstrumentor->SchemaCount() == 0))
+ if (minimalProbeMode && (fgCountInstrumentor->SchemaCount() == 1) && (fgHistogramInstrumentor->SchemaCount() == 0))
{
JITDUMP(
"Not instrumenting method: minimal probing enabled, and method has only one counter and no class probes\n");
@@ -1895,7 +1999,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
}
JITDUMP("Instrumenting method: %d count probes and %d class probes\n", fgCountInstrumentor->SchemaCount(),
- fgClassInstrumentor->SchemaCount());
+ fgHistogramInstrumentor->SchemaCount());
assert(schema.size() > 0);
@@ -1928,7 +2032,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
// Do any cleanup we might need to do...
//
fgCountInstrumentor->SuppressProbes();
- fgClassInstrumentor->SuppressProbes();
+ fgHistogramInstrumentor->SuppressProbes();
// If we needed to create cheap preds, we're done with them now.
//
@@ -1939,7 +2043,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
// We may have modified control flow preparing for instrumentation.
//
- const bool modifiedFlow = fgCountInstrumentor->ModifiedFlow() || fgClassInstrumentor->ModifiedFlow();
+ const bool modifiedFlow = fgCountInstrumentor->ModifiedFlow() || fgHistogramInstrumentor->ModifiedFlow();
return modifiedFlow ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
}
@@ -1954,22 +2058,25 @@ PhaseStatus Compiler::fgInstrumentMethod()
fgCountInstrumentor->Instrument(block, schema, profileMemory);
}
- if (fgClassInstrumentor->ShouldProcess(block))
+ if (fgHistogramInstrumentor->ShouldProcess(block))
{
- fgClassInstrumentor->Instrument(block, schema, profileMemory);
+ fgHistogramInstrumentor->Instrument(block, schema, profileMemory);
}
}
// Verify we instrumented everthing we created schemas for.
//
assert(fgCountInstrumentor->InstrCount() == fgCountInstrumentor->SchemaCount());
- assert(fgClassInstrumentor->InstrCount() == fgClassInstrumentor->SchemaCount());
+
+ // Verify we instrumented for each probe
+ //
+ assert(fgHistogramInstrumentor->InstrCount() == info.compHandleHistogramProbeCount);
// Add any special entry instrumentation. This does not
// use the schema mechanism.
//
fgCountInstrumentor->InstrumentMethodEntry(schema, profileMemory);
- fgClassInstrumentor->InstrumentMethodEntry(schema, profileMemory);
+ fgHistogramInstrumentor->InstrumentMethodEntry(schema, profileMemory);
// If we needed to create cheap preds, we're done with them now.
//
@@ -2052,6 +2159,10 @@ PhaseStatus Compiler::fgIncorporateProfileData()
fgPgoClassProfiles++;
break;
+ case ICorJitInfo::PgoInstrumentationKind::GetLikelyMethod:
+ fgPgoMethodProfiles++;
+ break;
+
case ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount:
case ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount:
if (iSchema + 1 < fgPgoSchemaCount)
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index da1bcd79595aa9..33d6fab81e5aef 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -1528,7 +1528,7 @@ GenTree* Compiler::fgGetCritSectOfStaticMethod()
critSect = info.compCompHnd->getMethodSync(info.compMethodHnd, (void**)&pCrit);
noway_assert((!critSect) != (!pCrit));
- tree = gtNewIconEmbHndNode(critSect, pCrit, GTF_ICON_METHOD_HDL, info.compMethodHnd);
+ tree = gtNewIconEmbHndNode(critSect, pCrit, GTF_ICON_GLOBAL_PTR, info.compMethodHnd);
}
else
{
@@ -3421,6 +3421,7 @@ PhaseStatus Compiler::fgDetermineFirstColdBlock()
{
firstColdBlock = fgFirstBB->bbNext;
prevToFirstColdBlock = fgFirstBB;
+ JITDUMP("JitStressProcedureSplitting is enabled: Splitting after the first basic block\n");
}
else
{
@@ -4129,47 +4130,6 @@ void Compiler::fgSetBlockOrder(BasicBlock* block)
return firstNode;
}
-/*static*/ Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data)
-{
- GenTree* tree = *pTree;
-
- // If this tree doesn't have the EXCEPT flag set, then there is no
- // way any of the child nodes could throw, so we can stop recursing.
- if (!(tree->gtFlags & GTF_EXCEPT))
- {
- return Compiler::WALK_SKIP_SUBTREES;
- }
-
- switch (tree->gtOper)
- {
- case GT_MUL:
- case GT_ADD:
- case GT_SUB:
- case GT_CAST:
- if (tree->gtOverflow())
- {
- return Compiler::WALK_ABORT;
- }
- break;
-
- case GT_INDEX_ADDR:
- // This calls CORINFO_HELP_RNGCHKFAIL for Debug code.
- if (tree->AsIndexAddr()->IsBoundsChecked())
- {
- return Compiler::WALK_ABORT;
- }
- break;
-
- case GT_BOUNDS_CHECK:
- return Compiler::WALK_ABORT;
-
- default:
- break;
- }
-
- return Compiler::WALK_CONTINUE;
-}
-
/*static*/ Compiler::fgWalkResult Compiler::fgChkLocAllocCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;
diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp
index 4c3fae5f1d9f9e..e6e2c84e807622 100644
--- a/src/coreclr/jit/forwardsub.cpp
+++ b/src/coreclr/jit/forwardsub.cpp
@@ -504,12 +504,6 @@ bool Compiler::fgForwardSubStatement(Statement* stmt)
return false;
}
- if (gtGetStructHandleIfPresent(fwdSubNode) != gtGetStructHandleIfPresent(lhsNode))
- {
- JITDUMP(" would change struct handle (assignment)\n");
- return false;
- }
-
// If lhs is mulit-reg, rhs must be too.
//
if (lhsNode->IsMultiRegNode() && !fwdSubNode->IsMultiRegNode())
@@ -665,14 +659,42 @@ bool Compiler::fgForwardSubStatement(Statement* stmt)
// Quirks:
//
- // We may sometimes lose or change a type handle. Avoid substituting if so.
+ // Don't substitute nodes "AddFinalArgsAndDetermineABIInfo" doesn't handle into struct args.
//
- if (gtGetStructHandleIfPresent(fwdSubNode) != gtGetStructHandleIfPresent(fsv.GetNode()))
+ if (fsv.IsCallArg() && fsv.GetNode()->TypeIs(TYP_STRUCT) &&
+ !fwdSubNode->OperIs(GT_OBJ, GT_LCL_VAR, GT_LCL_FLD, GT_MKREFANY))
{
- JITDUMP(" would change struct handle (substitution)\n");
+ JITDUMP(" use is a struct arg; fwd sub node is not OBJ/LCL_VAR/LCL_FLD/MKREFANY\n");
return false;
}
+ // We may sometimes lose or change a type handle. Avoid substituting if so.
+ //
+ // However, we allow free substitution of hardware SIMD types.
+ //
+ CORINFO_CLASS_HANDLE fwdHnd = gtGetStructHandleIfPresent(fwdSubNode);
+ CORINFO_CLASS_HANDLE useHnd = gtGetStructHandleIfPresent(fsv.GetNode());
+ if (fwdHnd != useHnd)
+ {
+ if ((fwdHnd == NO_CLASS_HANDLE) || (useHnd == NO_CLASS_HANDLE))
+ {
+ JITDUMP(" would add/remove struct handle (substitution)\n");
+ return false;
+ }
+
+#ifdef FEATURE_SIMD
+ const bool bothHWSIMD = isHWSIMDClass(fwdHnd) && isHWSIMDClass(useHnd);
+#else
+ const bool bothHWSIMD = false;
+#endif
+
+ if (!bothHWSIMD)
+ {
+ JITDUMP(" would change struct handle (substitution)\n");
+ return false;
+ }
+ }
+
#ifdef FEATURE_SIMD
// Don't forward sub a SIMD call under a HW intrinsic node.
// LowerCallStruct is not prepared for this.
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index f6a167a50e1390..17842a4195f029 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -567,14 +567,50 @@ void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData)
}
}
+//-----------------------------------------------------------
+// GetLayout: Get the struct layout for this node.
+//
+// Arguments:
+// compiler - The Compiler instance
+//
+// Return Value:
+// The struct layout of this node; it must have one.
+//
+// Notes:
+// This is the "general" method for getting the layout,
+// the more efficient node-specific ones should be used
+// in case the node's oper is known.
+//
+ClassLayout* GenTree::GetLayout(Compiler* compiler) const
+{
+ assert(varTypeIsStruct(TypeGet()));
+
+ switch (OperGet())
+ {
+ case GT_LCL_VAR:
+ return compiler->lvaGetDesc(AsLclVar())->GetLayout();
+
+ case GT_LCL_FLD:
+ return AsLclFld()->GetLayout();
+
+ case GT_OBJ:
+ case GT_BLK:
+ return AsBlk()->GetLayout();
+
+ case GT_MKREFANY:
+ return compiler->typGetObjLayout(compiler->impGetRefAnyClass());
+
+ default:
+ unreached();
+ }
+}
+
//-----------------------------------------------------------
// CopyReg: Copy the _gtRegNum/gtRegTag fields.
//
// Arguments:
// from - GenTree node from which to copy
//
-// Return Value:
-// None
void GenTree::CopyReg(GenTree* from)
{
_gtRegNum = from->_gtRegNum;
@@ -2396,7 +2432,8 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
case GT_LCL_FLD:
if ((op1->AsLclFld()->GetLclNum() != op2->AsLclFld()->GetLclNum()) ||
- (op1->AsLclFld()->GetLclOffs() != op2->AsLclFld()->GetLclOffs()))
+ (op1->AsLclFld()->GetLclOffs() != op2->AsLclFld()->GetLclOffs()) ||
+ (op1->AsLclFld()->GetLayout() != op2->AsLclFld()->GetLayout()))
{
break;
}
@@ -2792,6 +2829,7 @@ unsigned Compiler::gtHashValue(GenTree* tree)
break;
case GT_LCL_FLD:
hash = genTreeHashAdd(hash, tree->AsLclFld()->GetLclNum());
+ hash = genTreeHashAdd(hash, tree->AsLclFld()->GetLayout());
add = tree->AsLclFld()->GetLclOffs();
break;
@@ -4399,6 +4437,11 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
{
costSz = 10;
costEx = 2;
+ if (con->IsIconHandle())
+ {
+ // A sort of a hint for CSE to try harder for class handles
+ costEx += 1;
+ }
}
#endif // TARGET_AMD64
else
@@ -4640,6 +4683,11 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
costEx += 1;
costSz += 1;
}
+ else if (tree->TypeIs(TYP_STRUCT))
+ {
+ costEx += IND_COST_EX;
+ costSz += 2;
+ }
break;
case GT_LCL_FLD_ADDR:
@@ -6306,16 +6354,20 @@ bool GenTree::OperIsImplicitIndir() const
}
//------------------------------------------------------------------------------
-// OperMayThrow : Check whether the operation may throw.
+// OperExceptions : Get exception set this tree may throw.
//
//
// Arguments:
// comp - Compiler instance
//
// Return Value:
-// True if the given operator may cause an exception
-
-bool GenTree::OperMayThrow(Compiler* comp)
+// A bit set of exceptions this tree may throw.
+//
+// Remarks:
+// Should not be used on calls given that we can say nothing precise about
+// those.
+//
+ExceptionSetFlags GenTree::OperExceptions(Compiler* comp)
{
GenTree* op;
@@ -6332,26 +6384,40 @@ bool GenTree::OperMayThrow(Compiler* comp)
if (varTypeIsFloating(op->TypeGet()))
{
- return false; // Floating point division does not throw.
+ return ExceptionSetFlags::None;
}
// For integers only division by 0 or by -1 can throw
- if (op->IsIntegralConst() && !op->IsIntegralConst(0) && !op->IsIntegralConst(-1))
+ if (op->IsIntegralConst())
{
- return false;
+ if (op->IsIntegralConst(0))
+ {
+ return ExceptionSetFlags::DivideByZeroException;
+ }
+ if (op->IsIntegralConst(-1))
+ {
+ return ExceptionSetFlags::ArithmeticException;
+ }
+
+ return ExceptionSetFlags::None;
}
- return true;
+
+ return ExceptionSetFlags::DivideByZeroException | ExceptionSetFlags::ArithmeticException;
case GT_INTRINSIC:
// If this is an intrinsic that represents the object.GetType(), it can throw an NullReferenceException.
// Currently, this is the only intrinsic that can throw an exception.
- return AsIntrinsic()->gtIntrinsicName == NI_System_Object_GetType;
+ if (AsIntrinsic()->gtIntrinsicName == NI_System_Object_GetType)
+ {
+ return ExceptionSetFlags::NullReferenceException;
+ }
+
+ return ExceptionSetFlags::None;
case GT_CALL:
+ assert(!"Unexpected GT_CALL in OperExceptions");
- CorInfoHelpFunc helper;
- helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd);
- return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper));
+ return ExceptionSetFlags::All;
case GT_IND:
case GT_BLK:
@@ -6359,14 +6425,28 @@ bool GenTree::OperMayThrow(Compiler* comp)
case GT_NULLCHECK:
case GT_STORE_BLK:
case GT_STORE_DYN_BLK:
- return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr()));
+ if (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr()))
+ {
+ return ExceptionSetFlags::NullReferenceException;
+ }
+
+ return ExceptionSetFlags::None;
case GT_ARR_LENGTH:
- return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) &&
- comp->fgAddrCouldBeNull(this->AsArrLen()->ArrRef()));
+ if (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsArrLen()->ArrRef()))
+ {
+ return ExceptionSetFlags::NullReferenceException;
+ }
+
+ return ExceptionSetFlags::None;
case GT_ARR_ELEM:
- return comp->fgAddrCouldBeNull(this->AsArrElem()->gtArrObj);
+ if (comp->fgAddrCouldBeNull(this->AsArrElem()->gtArrObj))
+ {
+ return ExceptionSetFlags::NullReferenceException;
+ }
+
+ return ExceptionSetFlags::None;
case GT_FIELD:
{
@@ -6374,19 +6454,28 @@ bool GenTree::OperMayThrow(Compiler* comp)
if (fldObj != nullptr)
{
- return comp->fgAddrCouldBeNull(fldObj);
+ if (comp->fgAddrCouldBeNull(fldObj))
+ {
+ return ExceptionSetFlags::NullReferenceException;
+ }
}
- return false;
+ return ExceptionSetFlags::None;
}
case GT_BOUNDS_CHECK:
+ case GT_INDEX_ADDR:
+ return ExceptionSetFlags::IndexOutOfRangeException;
+
case GT_ARR_INDEX:
case GT_ARR_OFFSET:
- case GT_LCLHEAP:
+ return ExceptionSetFlags::NullReferenceException;
+
case GT_CKFINITE:
- case GT_INDEX_ADDR:
- return true;
+ return ExceptionSetFlags::ArithmeticException;
+
+ case GT_LCLHEAP:
+ return ExceptionSetFlags::StackOverflowException;
#ifdef FEATURE_HW_INTRINSICS
case GT_HWINTRINSIC:
@@ -6398,23 +6487,42 @@ bool GenTree::OperMayThrow(Compiler* comp)
// This operation contains an implicit indirection
// it could throw a null reference exception.
//
- return true;
+ return ExceptionSetFlags::NullReferenceException;
}
- break;
+
+ return ExceptionSetFlags::None;
}
#endif // FEATURE_HW_INTRINSICS
default:
- break;
- }
+ if (gtOverflowEx())
+ {
+ return ExceptionSetFlags::OverflowException;
+ }
- /* Overflow arithmetic operations also throw exceptions */
+ return ExceptionSetFlags::None;
+ }
+}
- if (gtOverflowEx())
+//------------------------------------------------------------------------------
+// OperMayThrow : Check whether the operation may throw.
+//
+//
+// Arguments:
+// comp - Compiler instance
+//
+// Return Value:
+// True if the given operator may cause an exception
+//
+bool GenTree::OperMayThrow(Compiler* comp)
+{
+ if (OperIs(GT_CALL))
{
- return true;
+ CorInfoHelpFunc helper;
+ helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd);
+ return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper));
}
- return false;
+ return OperExceptions(comp) != ExceptionSetFlags::None;
}
//-----------------------------------------------------------------------------------
@@ -6745,8 +6853,8 @@ GenTree* Compiler::gtNewIndOfIconHandleNode(var_types indType, size_t addr, GenT
GenTree* Compiler::gtNewIconEmbHndNode(void* value, void* pValue, GenTreeFlags iconFlags, void* compileTimeHandle)
{
- GenTree* iconNode;
- GenTree* handleNode;
+ GenTreeIntCon* iconNode;
+ GenTree* handleNode;
if (value != nullptr)
{
@@ -6779,7 +6887,13 @@ GenTree* Compiler::gtNewIconEmbHndNode(void* value, void* pValue, GenTreeFlags i
handleNode->gtFlags |= GTF_IND_INVARIANT;
}
- iconNode->AsIntCon()->gtCompileTimeHandle = (size_t)compileTimeHandle;
+ iconNode->gtCompileTimeHandle = (size_t)compileTimeHandle;
+#ifdef DEBUG
+ if (iconFlags == GTF_ICON_FTN_ADDR)
+ {
+ iconNode->gtTargetHandle = (size_t)compileTimeHandle;
+ }
+#endif
return handleNode;
}
@@ -6819,8 +6933,10 @@ GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue)
tree = gtNewOperNode(GT_IND, TYP_REF, tree);
// This indirection won't cause an exception.
tree->gtFlags |= GTF_IND_NONFAULTING;
- // This indirection points into the gloabal heap (it is String Object)
- tree->gtFlags |= GTF_GLOB_REF;
+ // String literal objects are also ok to model as invariant.
+ tree->gtFlags |= GTF_IND_INVARIANT;
+ // ..and they are never null.
+ tree->gtFlags |= GTF_IND_NONNULL;
break;
default:
@@ -9110,7 +9226,10 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
m_state = -1;
return;
- // Standard unary operators
+// Standard unary operators
+#ifdef TARGET_ARM64
+ case GT_CNEG_LT:
+#endif // TARGET_ARM64
case GT_STORE_LCL_VAR:
case GT_STORE_LCL_FLD:
case GT_NOT:
@@ -16057,7 +16176,7 @@ bool Compiler::gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call)
//
// Return Value:
// True if so
-
+//
bool Compiler::gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper)
{
CorInfoHelpFunc helper = CORINFO_HELP_UNDEF;
@@ -16084,6 +16203,61 @@ bool Compiler::gtIsActiveCSE_Candidate(GenTree* tree)
return (optValnumCSE_phase && IS_CSE_INDEX(tree->gtCSEnum));
}
+//------------------------------------------------------------------------
+// gtCollectExceptions: walk a tree collecting a bit set of exceptions the tree
+// may throw.
+//
+// Arguments:
+// tree - tree to examine
+//
+// Return Value:
+// Bit set of exceptions the tree may throw.
+//
+ExceptionSetFlags Compiler::gtCollectExceptions(GenTree* tree)
+{
+ class ExceptionsWalker final : public GenTreeVisitor
+ {
+ ExceptionSetFlags m_preciseExceptions = ExceptionSetFlags::None;
+
+ public:
+ ExceptionsWalker(Compiler* comp) : GenTreeVisitor(comp)
+ {
+ }
+
+ enum
+ {
+ DoPreOrder = true,
+ };
+
+ ExceptionSetFlags GetFlags()
+ {
+ return m_preciseExceptions;
+ }
+
+ fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
+ {
+ GenTree* tree = *use;
+ if ((tree->gtFlags & GTF_EXCEPT) == 0)
+ {
+ return WALK_SKIP_SUBTREES;
+ }
+
+ m_preciseExceptions |= tree->OperExceptions(m_compiler);
+ return WALK_CONTINUE;
+ }
+ };
+
+ // We only expect the caller to ask for precise exceptions for cases where
+ // it may help with disambiguating between exceptions. If the tree contains
+ // a call it can always throw arbitrary exceptions.
+ assert((tree->gtFlags & GTF_CALL) == 0);
+
+ ExceptionsWalker walker(this);
+ walker.WalkTree(&tree, nullptr);
+ assert(((tree->gtFlags & GTF_EXCEPT) == 0) || (walker.GetFlags() != ExceptionSetFlags::None));
+ return walker.GetFlags();
+}
+
/*****************************************************************************/
struct ComplexityStruct
@@ -16402,7 +16576,7 @@ const GenTreeLclVarCommon* GenTree::IsLocalAddrExpr() const
//
GenTreeLclVar* GenTree::IsImplicitByrefParameterValue(Compiler* compiler)
{
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
+#if FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64) // TODO-LOONGARCH64-CQ: enable this.
GenTreeLclVar* lcl = nullptr;
@@ -16434,7 +16608,7 @@ GenTreeLclVar* GenTree::IsImplicitByrefParameterValue(Compiler* compiler)
return lcl;
}
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64)
+#endif // FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64)
return nullptr;
}
@@ -17194,7 +17368,11 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree)
}
#endif
}
+ else
#endif
+ {
+ structHnd = tree->AsLclFld()->GetLayout()->GetClassHandle();
+ }
break;
case GT_LCL_VAR:
{
@@ -18402,14 +18580,16 @@ var_types GenTreeJitIntrinsic::GetSimdBaseType() const
return JitType2PreciseVarType(simdBaseJitType);
}
-// Returns true for the SIMD Intrinsic instructions that have MemoryLoad semantics, false otherwise
+//------------------------------------------------------------------------
+// OperIsMemoryLoad: Does this SIMD intrinsic have memory load semantics?
+//
+// Return Value:
+// Whether this intrinsic may throw NullReferenceException if the
+// address is "null".
+//
bool GenTreeSIMD::OperIsMemoryLoad() const
{
- if (GetSIMDIntrinsicId() == SIMDIntrinsicInitArray)
- {
- return true;
- }
- return false;
+ return GetSIMDIntrinsicId() == SIMDIntrinsicInitArray;
}
// TODO-Review: why are layouts not compared here?
@@ -22408,26 +22588,56 @@ GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(
/* isSimdAsHWIntrinsic */ false, op1, op2, op3);
}
-// Returns true for the HW Intrinsic instructions that have MemoryLoad semantics, false otherwise
-bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
+//------------------------------------------------------------------------
+// OperIsMemoryLoad: Does this HWI node have memory load semantics?
+//
+// Arguments:
+// pAddr - optional [out] parameter for the address
+//
+// Return Value:
+// Whether this intrinsic may throw NullReferenceException if the
+// address is "null".
+//
+bool GenTreeHWIntrinsic::OperIsMemoryLoad(GenTree** pAddr) const
{
+ GenTree* addr = nullptr;
+
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
NamedIntrinsic intrinsicId = GetHWIntrinsicId();
HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId);
if (category == HW_Category_MemoryLoad)
{
- return true;
+ switch (intrinsicId)
+ {
+#ifdef TARGET_XARCH
+ case NI_SSE_LoadLow:
+ case NI_SSE_LoadHigh:
+ case NI_SSE2_LoadLow:
+ case NI_SSE2_LoadHigh:
+ addr = Op(2);
+ break;
+#endif // TARGET_XARCH
+
+#ifdef TARGET_ARM64
+ case NI_AdvSimd_LoadAndInsertScalar:
+ addr = Op(3);
+ break;
+#endif // TARGET_ARM64
+
+ default:
+ addr = Op(1);
+ break;
+ }
}
#ifdef TARGET_XARCH
- else if (HWIntrinsicInfo::MaybeMemoryLoad(GetHWIntrinsicId()))
+ else if (HWIntrinsicInfo::MaybeMemoryLoad(intrinsicId))
{
// Some intrinsics (without HW_Category_MemoryLoad) also have MemoryLoad semantics
// This is generally because they have both vector and pointer overloads, e.g.,
// * Vector128 BroadcastScalarToVector128(Vector128 value)
// * Vector128 BroadcastScalarToVector128(byte* source)
- // So, we need to check the argument's type is memory-reference or Vector128
-
+ //
if ((category == HW_Category_SimpleSIMD) || (category == HW_Category_SIMDScalar))
{
assert(GetOperandCount() == 1);
@@ -22442,53 +22652,91 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
case NI_AVX2_ConvertToVector256Int16:
case NI_AVX2_ConvertToVector256Int32:
case NI_AVX2_ConvertToVector256Int64:
- {
- CorInfoType auxiliaryType = GetAuxiliaryJitType();
-
- if (auxiliaryType == CORINFO_TYPE_PTR)
+ if (GetAuxiliaryJitType() == CORINFO_TYPE_PTR)
{
- return true;
+ addr = Op(1);
}
-
- assert(auxiliaryType == CORINFO_TYPE_UNDEF);
- return false;
- }
+ else
+ {
+ assert(GetAuxiliaryJitType() == CORINFO_TYPE_UNDEF);
+ }
+ break;
default:
- {
unreached();
- }
}
}
else if (category == HW_Category_IMM)
{
- // Do we have less than 3 operands?
- if (GetOperandCount() < 3)
- {
- return false;
- }
- else if (HWIntrinsicInfo::isAVX2GatherIntrinsic(GetHWIntrinsicId()))
+ switch (intrinsicId)
{
- return true;
+ case NI_AVX2_GatherVector128:
+ case NI_AVX2_GatherVector256:
+ addr = Op(1);
+ break;
+
+ case NI_AVX2_GatherMaskVector128:
+ case NI_AVX2_GatherMaskVector256:
+ addr = Op(2);
+ break;
+
+ default:
+ break;
}
}
}
#endif // TARGET_XARCH
#endif // TARGET_XARCH || TARGET_ARM64
+
+ if (pAddr != nullptr)
+ {
+ *pAddr = addr;
+ }
+
+ if (addr != nullptr)
+ {
+ assert(varTypeIsI(addr));
+ return true;
+ }
+
return false;
}
-// Returns true for the HW Intrinsic instructions that have MemoryStore semantics, false otherwise
-bool GenTreeHWIntrinsic::OperIsMemoryStore() const
+//------------------------------------------------------------------------
+// OperIsMemoryLoad: Does this HWI node have memory store semantics?
+//
+// Arguments:
+// pAddr - optional [out] parameter for the address
+//
+// Return Value:
+// Whether this intrinsic may mutate heap state and/or throw a
+// NullReferenceException if the address is "null".
+//
+bool GenTreeHWIntrinsic::OperIsMemoryStore(GenTree** pAddr) const
{
+ GenTree* addr = nullptr;
+
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(GetHWIntrinsicId());
+ NamedIntrinsic intrinsicId = GetHWIntrinsicId();
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId);
+
if (category == HW_Category_MemoryStore)
{
- return true;
+ switch (intrinsicId)
+ {
+#ifdef TARGET_XARCH
+ case NI_SSE2_MaskMove:
+ addr = Op(3);
+ break;
+#endif // TARGET_XARCH
+
+ default:
+ addr = Op(1);
+ break;
+ }
}
#ifdef TARGET_XARCH
- else if (HWIntrinsicInfo::MaybeMemoryStore(GetHWIntrinsicId()) &&
+ else if (HWIntrinsicInfo::MaybeMemoryStore(intrinsicId) &&
(category == HW_Category_IMM || category == HW_Category_Scalar))
{
// Some intrinsics (without HW_Category_MemoryStore) also have MemoryStore semantics
@@ -22499,29 +22747,44 @@ bool GenTreeHWIntrinsic::OperIsMemoryStore() const
// So, the 3-argument form is MemoryStore
if (GetOperandCount() == 3)
{
- switch (GetHWIntrinsicId())
+ switch (intrinsicId)
{
case NI_BMI2_MultiplyNoFlags:
case NI_BMI2_X64_MultiplyNoFlags:
- return true;
+ addr = Op(3);
+ break;
+
default:
- return false;
+ break;
}
}
}
#endif // TARGET_XARCH
#endif // TARGET_XARCH || TARGET_ARM64
+
+ if (pAddr != nullptr)
+ {
+ *pAddr = addr;
+ }
+
+ if (addr != nullptr)
+ {
+ assert(varTypeIsI(addr));
+ return true;
+ }
+
return false;
}
-// Returns true for the HW Intrinsic instructions that have MemoryLoad or MemoryStore semantics, false otherwise
+//------------------------------------------------------------------------
+// OperIsMemoryLoadOrStore: Does this HWI node have memory load or store semantics?
+//
+// Return Value:
+// Whether "this" is "OperIsMemoryLoad" or "OperIsMemoryStore".
+//
bool GenTreeHWIntrinsic::OperIsMemoryLoadOrStore() const
{
-#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
return OperIsMemoryLoad() || OperIsMemoryStore();
-#else
- return false;
-#endif
}
NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicId() const
@@ -23122,7 +23385,7 @@ unsigned GenTreeHWIntrinsic::GetResultOpNumForFMA(GenTree* use, GenTree* op1, Ge
unsigned GenTreeLclFld::GetSize() const
{
- return genTypeSize(TypeGet());
+ return TypeIs(TYP_STRUCT) ? GetLayout()->GetSize() : genTypeSize(TypeGet());
}
#ifdef TARGET_ARM
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index b9ba82532a5541..afd71a684725b7 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -130,6 +130,45 @@ enum gtCallTypes : BYTE
CT_COUNT // fake entry (must be last)
};
+enum class ExceptionSetFlags : uint32_t
+{
+ None = 0x0,
+ OverflowException = 0x1,
+ DivideByZeroException = 0x2,
+ ArithmeticException = 0x4,
+ NullReferenceException = 0x8,
+ IndexOutOfRangeException = 0x10,
+ StackOverflowException = 0x20,
+
+ All = OverflowException | DivideByZeroException | ArithmeticException | NullReferenceException |
+ IndexOutOfRangeException | StackOverflowException,
+};
+
+inline constexpr ExceptionSetFlags operator~(ExceptionSetFlags a)
+{
+ return (ExceptionSetFlags)(~(uint32_t)a);
+}
+
+inline constexpr ExceptionSetFlags operator|(ExceptionSetFlags a, ExceptionSetFlags b)
+{
+ return (ExceptionSetFlags)((uint32_t)a | (uint32_t)b);
+}
+
+inline constexpr ExceptionSetFlags operator&(ExceptionSetFlags a, ExceptionSetFlags b)
+{
+ return (ExceptionSetFlags)((uint32_t)a & (uint32_t)b);
+}
+
+inline ExceptionSetFlags& operator|=(ExceptionSetFlags& a, ExceptionSetFlags b)
+{
+ return a = (ExceptionSetFlags)((uint32_t)a | (uint32_t)b);
+}
+
+inline ExceptionSetFlags& operator&=(ExceptionSetFlags& a, ExceptionSetFlags b)
+{
+ return a = (ExceptionSetFlags)((uint32_t)a & (uint32_t)b);
+}
+
#ifdef DEBUG
/*****************************************************************************
*
@@ -150,7 +189,7 @@ struct BasicBlock;
enum BasicBlockFlags : unsigned __int64;
struct InlineCandidateInfo;
struct GuardedDevirtualizationCandidateInfo;
-struct ClassProfileCandidateInfo;
+struct HandleHistogramProfileCandidateInfo;
struct LateDevirtualizationInfo;
typedef unsigned short AssertionIndex;
@@ -408,7 +447,12 @@ enum GenTreeFlags : unsigned int
GTF_NOREG_AT_USE = 0x00000100, // tree node is in memory at the point of use
GTF_SET_FLAGS = 0x00000200, // Requires that codegen for this node set the flags. Use gtSetFlags() to check this flag.
- GTF_USE_FLAGS = 0x00000400, // Indicates that this node uses the flags bits.
+
+#ifdef TARGET_XARCH
+ GTF_DONT_EXTEND = 0x00000400, // This small-typed tree produces a value with undefined upper bits. Used on x86/x64 as a
+ // lowering optimization and tells the codegen to use instructions like "mov al, [addr]"
+ // instead of "movzx/movsx", when the user node doesn't need the upper bits.
+#endif // TARGET_XARCH
GTF_MAKE_CSE = 0x00000800, // Hoisted expression: try hard to make this into CSE (see optPerformHoistExpr)
GTF_DONT_CSE = 0x00001000, // Don't bother CSE'ing this expr
@@ -526,16 +570,9 @@ enum GenTreeFlags : unsigned int
// alignment of 1 byte)
GTF_IND_INVARIANT = 0x01000000, // GT_IND -- the target is invariant (a prejit indirection)
GTF_IND_NONNULL = 0x00400000, // GT_IND -- the indirection never returns null (zero)
-#if defined(TARGET_XARCH)
- GTF_IND_DONT_EXTEND = 0x00200000, // GT_IND -- the indirection does not need to extend for small types
-#endif // TARGET_XARCH
GTF_IND_FLAGS = GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | GTF_IND_UNALIGNED | GTF_IND_INVARIANT |
- GTF_IND_NONNULL | GTF_IND_TGT_NOT_HEAP | GTF_IND_TGT_HEAP
-#if defined(TARGET_XARCH)
- | GTF_IND_DONT_EXTEND
-#endif // TARGET_XARCH
- ,
+ GTF_IND_NONNULL | GTF_IND_TGT_NOT_HEAP | GTF_IND_TGT_HEAP,
GTF_ADDRMODE_NO_CSE = 0x80000000, // GT_ADD/GT_MUL/GT_LSH -- Do not CSE this node only, forms complex
// addressing mode
@@ -605,11 +642,6 @@ enum GenTreeFlags : unsigned int
GTF_SIMDASHW_OP = 0x80000000, // GT_HWINTRINSIC -- Indicates that the structHandle should be gotten from gtGetStructHandleForSIMD
// rather than from gtGetStructHandleForHWSIMD.
-
- // Flag used by assertion prop to indicate that a type is a TYP_LONG
-#ifdef TARGET_64BIT
- GTF_ASSERTION_PROP_LONG = 0x00000001,
-#endif // TARGET_64BIT
};
inline constexpr GenTreeFlags operator ~(GenTreeFlags a)
@@ -764,6 +796,8 @@ struct GenTree
return gtType;
}
+ ClassLayout* GetLayout(Compiler* compiler) const;
+
#ifdef DEBUG
genTreeOps gtOperSave; // Only used to save gtOper when we destroy a node, to aid debugging.
#endif
@@ -1109,6 +1143,11 @@ struct GenTree
return true;
}
+ bool IsNotGcDef() const
+ {
+ return IsIntegralConst(0) || IsLocalAddrExpr();
+ }
+
// LIR flags
// These helper methods, along with the flag values they manipulate, are defined in lir.h
//
@@ -1815,6 +1854,7 @@ struct GenTree
bool OperRequiresCallFlag(Compiler* comp);
bool OperMayThrow(Compiler* comp);
+ ExceptionSetFlags OperExceptions(Compiler* comp);
unsigned GetScaleIndexMul();
unsigned GetScaleIndexShf();
@@ -2010,6 +2050,25 @@ struct GenTree
gtFlags &= ~GTF_REVERSE_OPS;
}
+#if defined(TARGET_XARCH)
+ void SetDontExtend()
+ {
+ assert(varTypeIsSmall(TypeGet()) && OperIs(GT_IND, GT_LCL_FLD));
+ gtFlags |= GTF_DONT_EXTEND;
+ }
+
+ void ClearDontExtend()
+ {
+ gtFlags &= ~GTF_DONT_EXTEND;
+ }
+
+ bool DontExtend() const
+ {
+ assert(varTypeIsSmall(TypeGet()) || ((gtFlags & GTF_DONT_EXTEND) == 0));
+ return (gtFlags & GTF_DONT_EXTEND) != 0;
+ }
+#endif // TARGET_XARCH
+
bool IsUnsigned() const
{
return ((gtFlags & GTF_UNSIGNED) != 0);
@@ -3056,8 +3115,7 @@ struct GenTreeIntCon : public GenTreeIntConCommon
FieldSeqNode* gtFieldSeq;
#ifdef DEBUG
- // If the value represents target address, holds the method handle to that target which is used
- // to fetch target method name and display in the disassembled code.
+ // If the value represents target address (for a field or call), holds the handle of the field (or call).
size_t gtTargetHandle = 0;
#endif
@@ -3677,9 +3735,10 @@ struct GenTreeLclFld : public GenTreeLclVarCommon
public:
GenTreeLclFld(genTreeOps oper, var_types type, unsigned lclNum, unsigned lclOffs, ClassLayout* layout = nullptr)
- : GenTreeLclVarCommon(oper, type, lclNum), m_lclOffs(static_cast(lclOffs)), m_layout(layout)
+ : GenTreeLclVarCommon(oper, type, lclNum), m_lclOffs(static_cast(lclOffs))
{
assert(lclOffs <= UINT16_MAX);
+ SetLayout(layout);
}
uint16_t GetLclOffs() const
@@ -3695,6 +3754,7 @@ struct GenTreeLclFld : public GenTreeLclVarCommon
ClassLayout* GetLayout() const
{
+ assert(!TypeIs(TYP_STRUCT) || (m_layout != nullptr));
return m_layout;
}
@@ -4563,6 +4623,7 @@ class CallArgs
void RemovedWellKnownArg(WellKnownArg arg);
regNumber GetCustomRegister(Compiler* comp, CorInfoCallConvExtension cc, WellKnownArg arg);
void SplitArg(CallArg* arg, unsigned numRegs, unsigned numSlots);
+ void SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs);
public:
CallArgs();
@@ -4607,7 +4668,6 @@ class CallArgs
void AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call);
void ArgsComplete(Compiler* comp, GenTreeCall* call);
- void SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs);
void EvalArgsToTemps(Compiler* comp, GenTreeCall* call);
void SetNeedsTemp(CallArg* arg);
bool IsNonStandard(Compiler* comp, GenTreeCall* call, CallArg* arg);
@@ -5308,7 +5368,7 @@ struct GenTreeCall final : public GenTree
#if defined(TARGET_ARMARCH)
// For ARM architectures, we always use an indirection cell for R2R calls.
- if (IsR2RRelativeIndir())
+ if (IsR2RRelativeIndir() && !IsDelegateInvoke())
{
return WellKnownArg::R2RIndirectionCell;
}
@@ -5371,7 +5431,7 @@ struct GenTreeCall final : public GenTree
// gtInlineCandidateInfo is only used when inlining methods
InlineCandidateInfo* gtInlineCandidateInfo;
GuardedDevirtualizationCandidateInfo* gtGuardedDevirtualizationCandidateInfo;
- ClassProfileCandidateInfo* gtClassProfileCandidateInfo;
+ HandleHistogramProfileCandidateInfo* gtHandleHistogramProfileCandidateInfo;
LateDevirtualizationInfo* gtLateDevirtualizationInfo;
CORINFO_GENERIC_HANDLE compileTimeHelperArgumentHandle; // Used to track type handle argument of dynamic helpers
void* gtDirectCallAddress; // Used to pass direct call address between lower and codegen
@@ -6069,8 +6129,7 @@ struct GenTreeSIMD : public GenTreeJitIntrinsic
}
#endif
- bool OperIsMemoryLoad() const; // Returns true for the SIMD Intrinsic instructions that have MemoryLoad semantics,
- // false otherwise
+ bool OperIsMemoryLoad() const;
SIMDIntrinsicID GetSIMDIntrinsicId() const
{
@@ -6114,12 +6173,10 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic
}
#endif
- bool OperIsMemoryLoad() const; // Returns true for the HW Intrinsic instructions that have MemoryLoad semantics,
- // false otherwise
- bool OperIsMemoryStore() const; // Returns true for the HW Intrinsic instructions that have MemoryStore semantics,
- // false otherwise
- bool OperIsMemoryLoadOrStore() const; // Returns true for the HW Intrinsic instructions that have MemoryLoad or
- // MemoryStore semantics, false otherwise
+ bool OperIsMemoryLoad(GenTree** pAddr = nullptr) const;
+ bool OperIsMemoryStore(GenTree** pAddr = nullptr) const;
+ bool OperIsMemoryLoadOrStore() const;
+
bool IsSimdAsHWIntrinsic() const
{
return (gtFlags & GTF_SIMDASHW_OP) != 0;
@@ -6764,23 +6821,6 @@ struct GenTreeIndir : public GenTreeOp
return (gtFlags & GTF_IND_UNALIGNED) != 0;
}
-#if defined(TARGET_XARCH)
- void SetDontExtend()
- {
- gtFlags |= GTF_IND_DONT_EXTEND;
- }
-
- void ClearDontExtend()
- {
- gtFlags &= ~GTF_IND_DONT_EXTEND;
- }
-
- bool DontExtend() const
- {
- return (gtFlags & GTF_IND_DONT_EXTEND) != 0;
- }
-#endif // TARGET_XARCH
-
#if DEBUGGABLE_GENTREE
// Used only for GenTree::GetVtableForOper()
GenTreeIndir() : GenTreeOp()
@@ -7441,12 +7481,18 @@ struct GenTreePutArgStk : public GenTreeUnOp
// TODO-Throughput: The following information should be obtained from the child
// block node.
- enum class Kind : __int8{
+ enum class Kind : int8_t{
Invalid, RepInstr, PartialRepInstr, Unroll, Push,
};
Kind gtPutArgStkKind;
-#endif
+#ifdef TARGET_XARCH
+private:
+ uint8_t m_argLoadSizeDelta;
+#endif // TARGET_XARCH
+#endif // FEATURE_PUT_STRUCT_ARG_STK
+
+public:
GenTreePutArgStk(genTreeOps oper,
var_types type,
GenTree* op1,
@@ -7472,7 +7518,10 @@ struct GenTreePutArgStk : public GenTreeUnOp
#endif // FEATURE_FASTTAILCALL
#if defined(FEATURE_PUT_STRUCT_ARG_STK)
, gtPutArgStkKind(Kind::Invalid)
+#if defined(TARGET_XARCH)
+ , m_argLoadSizeDelta(UINT8_MAX)
#endif
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
}
@@ -7519,6 +7568,36 @@ struct GenTreePutArgStk : public GenTreeUnOp
return m_byteSize;
}
+#ifdef TARGET_XARCH
+ //------------------------------------------------------------------------
+ // SetArgLoadSize: Set the optimal number of bytes to load for this argument.
+ //
+ // On XARCH, it is profitable to use wider loads when our source is a local
+ // variable. To not duplicate the logic between lowering, LSRA and codegen,
+ // we do the legality check once, in lowering, and save the result here, as
+ // a negative delta relative to the size of the argument with padding.
+ //
+ // Arguments:
+ // loadSize - The optimal number of bytes to load
+ //
+ void SetArgLoadSize(unsigned loadSize)
+ {
+ unsigned argSize = GetStackByteSize();
+ assert(roundUp(loadSize, TARGET_POINTER_SIZE) == argSize);
+
+ m_argLoadSizeDelta = static_cast(argSize - loadSize);
+ }
+
+ //------------------------------------------------------------------------
+ // GetArgLoadSize: Get the optimal number of bytes to load for this argument.
+ //
+ unsigned GetArgLoadSize() const
+ {
+ assert(m_argLoadSizeDelta != UINT8_MAX);
+ return GetStackByteSize() - m_argLoadSizeDelta;
+ }
+#endif // TARGET_XARCH
+
// Return true if this is a PutArgStk of a SIMD12 struct.
// This is needed because such values are re-typed to SIMD16, and the type of PutArgStk is VOID.
unsigned isSIMD12() const
diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h
index a089124825ff93..53a3618100b9e0 100644
--- a/src/coreclr/jit/gtlist.h
+++ b/src/coreclr/jit/gtlist.h
@@ -219,6 +219,7 @@ GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
GTNODE(ADDEX, GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Add with sign/zero extension.
GTNODE(BFIZ , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Bitfield Insert in Zero.
GTNODE(CSNEG_MI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Conditional select, negate, minus result
+GTNODE(CNEG_LT , GenTreeOp ,0,GTK_UNOP|DBK_NOTHIR) // Conditional, negate, signed less than result
#endif
//-----------------------------------------------------------------------------
diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp
index 0885892bcd6304..6ad3ef37cf8f1f 100644
--- a/src/coreclr/jit/hwintrinsic.cpp
+++ b/src/coreclr/jit/hwintrinsic.cpp
@@ -314,20 +314,35 @@ NamedIntrinsic HWIntrinsicInfo::lookupId(Compiler* comp,
return NI_Throw_PlatformNotSupportedException;
}
+ // Special case: For Vector64/128/256 we currently don't accelerate any of the methods when
+ // IsHardwareAccelerated reports false. For Vector64 and Vector128 this is when the baseline
+ // ISA is unsupported. For Vector256 this is when AVX2 is unsupported since integer types
+ // can't get properly accelerated.
+
+ if (isa == InstructionSet_Vector128)
+ {
+ if (!comp->IsBaselineSimdIsaSupported())
+ {
+ return NI_Illegal;
+ }
+ }
#if defined(TARGET_XARCH)
- if ((isa == InstructionSet_Vector128) || (isa == InstructionSet_Vector256))
+ else if (isa == InstructionSet_Vector256)
+ {
+ if (!comp->compOpportunisticallyDependsOn(InstructionSet_AVX2))
+ {
+ return NI_Illegal;
+ }
+ }
#elif defined(TARGET_ARM64)
- if ((isa == InstructionSet_Vector64) || (isa == InstructionSet_Vector128))
-#endif
+ else if (isa == InstructionSet_Vector64)
{
if (!comp->IsBaselineSimdIsaSupported())
{
- // Special case: For Vector64/128/256 we currently don't accelerate any of the methods when SSE/SSE2
- // aren't supported since IsHardwareAccelerated reports false. To simplify the importation logic we'll
- // just return illegal here and let it fallback to the software path instead.
return NI_Illegal;
}
}
+#endif
for (int i = 0; i < (NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START - 1); i++)
{
@@ -640,48 +655,15 @@ static bool isSupportedBaseType(NamedIntrinsic intrinsic, CorInfoType baseJitTyp
return true;
}
+#ifdef DEBUG
+ CORINFO_InstructionSet isa = HWIntrinsicInfo::lookupIsa(intrinsic);
#ifdef TARGET_XARCH
- assert((intrinsic == NI_Vector128_As) || (intrinsic == NI_Vector128_AsByte) ||
- (intrinsic == NI_Vector128_AsDouble) || (intrinsic == NI_Vector128_AsInt16) ||
- (intrinsic == NI_Vector128_AsInt32) || (intrinsic == NI_Vector128_AsInt64) ||
- (intrinsic == NI_Vector128_AsSByte) || (intrinsic == NI_Vector128_AsSingle) ||
- (intrinsic == NI_Vector128_AsUInt16) || (intrinsic == NI_Vector128_AsUInt32) ||
- (intrinsic == NI_Vector128_AsUInt64) || (intrinsic == NI_Vector128_get_AllBitsSet) ||
- (intrinsic == NI_Vector128_get_Count) || (intrinsic == NI_Vector128_get_Zero) ||
- (intrinsic == NI_Vector128_GetElement) || (intrinsic == NI_Vector128_WithElement) ||
- (intrinsic == NI_Vector128_ToScalar) || (intrinsic == NI_Vector128_ToVector256) ||
- (intrinsic == NI_Vector128_ToVector256Unsafe) || (intrinsic == NI_Vector256_As) ||
- (intrinsic == NI_Vector256_AsByte) || (intrinsic == NI_Vector256_AsDouble) ||
- (intrinsic == NI_Vector256_AsInt16) || (intrinsic == NI_Vector256_AsInt32) ||
- (intrinsic == NI_Vector256_AsInt64) || (intrinsic == NI_Vector256_AsSByte) ||
- (intrinsic == NI_Vector256_AsSingle) || (intrinsic == NI_Vector256_AsUInt16) ||
- (intrinsic == NI_Vector256_AsUInt32) || (intrinsic == NI_Vector256_AsUInt64) ||
- (intrinsic == NI_Vector256_get_AllBitsSet) || (intrinsic == NI_Vector256_get_Count) ||
- (intrinsic == NI_Vector256_get_Zero) || (intrinsic == NI_Vector256_GetElement) ||
- (intrinsic == NI_Vector256_WithElement) || (intrinsic == NI_Vector256_GetLower) ||
- (intrinsic == NI_Vector256_ToScalar));
+ assert((isa == InstructionSet_Vector256) || (isa == InstructionSet_Vector128));
#endif // TARGET_XARCH
#ifdef TARGET_ARM64
- assert((intrinsic == NI_Vector64_As) || (intrinsic == NI_Vector64_AsByte) || (intrinsic == NI_Vector64_AsDouble) ||
- (intrinsic == NI_Vector64_AsInt16) || (intrinsic == NI_Vector64_AsInt32) ||
- (intrinsic == NI_Vector64_AsInt64) || (intrinsic == NI_Vector64_AsSByte) ||
- (intrinsic == NI_Vector64_AsSingle) || (intrinsic == NI_Vector64_AsUInt16) ||
- (intrinsic == NI_Vector64_AsUInt32) || (intrinsic == NI_Vector64_AsUInt64) ||
- (intrinsic == NI_Vector64_get_AllBitsSet) || (intrinsic == NI_Vector64_get_Count) ||
- (intrinsic == NI_Vector64_get_Zero) || (intrinsic == NI_Vector64_GetElement) ||
- (intrinsic == NI_Vector64_ToScalar) || (intrinsic == NI_Vector64_ToVector128) ||
- (intrinsic == NI_Vector64_ToVector128Unsafe) || (intrinsic == NI_Vector64_WithElement) ||
- (intrinsic == NI_Vector128_As) || (intrinsic == NI_Vector128_AsByte) ||
- (intrinsic == NI_Vector128_AsDouble) || (intrinsic == NI_Vector128_AsInt16) ||
- (intrinsic == NI_Vector128_AsInt32) || (intrinsic == NI_Vector128_AsInt64) ||
- (intrinsic == NI_Vector128_AsSByte) || (intrinsic == NI_Vector128_AsSingle) ||
- (intrinsic == NI_Vector128_AsUInt16) || (intrinsic == NI_Vector128_AsUInt32) ||
- (intrinsic == NI_Vector128_AsUInt64) || (intrinsic == NI_Vector128_get_AllBitsSet) ||
- (intrinsic == NI_Vector128_get_Count) || (intrinsic == NI_Vector128_get_Zero) ||
- (intrinsic == NI_Vector128_GetElement) || (intrinsic == NI_Vector128_GetLower) ||
- (intrinsic == NI_Vector128_GetUpper) || (intrinsic == NI_Vector128_ToScalar) ||
- (intrinsic == NI_Vector128_WithElement));
+ assert((isa == InstructionSet_Vector64) || (isa == InstructionSet_Vector128));
#endif // TARGET_ARM64
+#endif // DEBUG
return false;
}
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 316ea1670e4edc..3c01e665ddc327 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -862,26 +862,10 @@ void Compiler::impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call)
// Morph trees that aren't already OBJs or MKREFANY to be OBJs
assert(ti.IsType(TI_STRUCT));
- bool forceNormalization = false;
- if (varTypeIsSIMD(argNode))
- {
- // We need to ensure that fgMorphArgs will use the correct struct handle to ensure proper
- // ABI handling of this argument.
- // Note that this can happen, for example, if we have a SIMD intrinsic that returns a SIMD type
- // with a different baseType than we've seen.
- // We also need to ensure an OBJ node if we have a FIELD node that might be transformed to LCL_FLD
- // or a plain GT_IND.
- // TODO-Cleanup: Consider whether we can eliminate all of these cases.
- if ((gtGetStructHandleIfPresent(argNode) != classHnd) || argNode->OperIs(GT_FIELD))
- {
- forceNormalization = true;
- }
- }
-
JITDUMP("Calling impNormStructVal on:\n");
DISPTREE(argNode);
- argNode = impNormStructVal(argNode, classHnd, spillCheckLevel, forceNormalization);
+ argNode = impNormStructVal(argNode, classHnd, spillCheckLevel);
// For SIMD types the normalization can normalize TYP_STRUCT to
// e.g. TYP_SIMD16 which we keep (along with the class handle) in
// the CallArgs.
@@ -1404,10 +1388,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
else if (src->OperIsBlk())
{
asgType = impNormStructType(structHnd);
- if (src->gtOper == GT_OBJ)
- {
- assert(src->AsObj()->GetLayout()->GetClassHandle() == structHnd);
- }
+ assert(ClassLayout::AreCompatible(src->AsBlk()->GetLayout(), typGetObjLayout(structHnd)));
}
else if (src->gtOper == GT_MKREFANY)
{
@@ -1650,7 +1631,7 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal,
// be modified to one that is handled specially by the JIT, possibly being a candidate
// for full enregistration, e.g. TYP_SIMD16. If the size of the struct is already known
// call structSizeMightRepresentSIMDType to determine if this api needs to be called.
-
+//
var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoType* pSimdBaseJitType)
{
assert(structHnd != NO_CLASS_HANDLE);
@@ -1694,7 +1675,6 @@ var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoTyp
// structVal - the node we are going to normalize
// structHnd - the class handle for the node
// curLevel - the current stack level
-// forceNormalization - Force the creation of an OBJ node (default is false).
//
// Notes:
// Given struct value 'structVal', make sure it is 'canonical', that is
@@ -1704,12 +1684,9 @@ var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoTyp
// - a node (e.g. GT_FIELD) that will be morphed.
// If the node is a CALL or RET_EXPR, a copy will be made to a new temp.
//
-GenTree* Compiler::impNormStructVal(GenTree* structVal,
- CORINFO_CLASS_HANDLE structHnd,
- unsigned curLevel,
- bool forceNormalization /*=false*/)
+GenTree* Compiler::impNormStructVal(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel)
{
- assert(forceNormalization || varTypeIsStruct(structVal));
+ assert(varTypeIsStruct(structVal));
assert(structHnd != NO_CLASS_HANDLE);
var_types structType = structVal->TypeGet();
bool makeTemp = false;
@@ -1723,27 +1700,20 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
genTreeOps oper = structVal->OperGet();
switch (oper)
{
- // GT_RETURN and GT_MKREFANY don't capture the handle.
- case GT_RETURN:
- break;
+ // GT_MKREFANY is supported directly by args morphing.
case GT_MKREFANY:
alreadyNormalized = true;
break;
case GT_CALL:
- structVal->AsCall()->gtRetClsHnd = structHnd;
- makeTemp = true;
- break;
-
case GT_RET_EXPR:
- structVal->AsRetExpr()->gtRetClsHnd = structHnd;
- makeTemp = true;
+ makeTemp = true;
break;
case GT_FIELD:
// Wrap it in a GT_OBJ, if needed.
structVal->gtType = structType;
- if ((structType == TYP_STRUCT) || forceNormalization)
+ if (structType == TYP_STRUCT)
{
structVal = gtNewObjNode(structHnd, gtNewOperNode(GT_ADDR, TYP_BYREF, structVal));
}
@@ -1758,7 +1728,6 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
case GT_OBJ:
case GT_BLK:
- case GT_ASG:
// These should already have the appropriate type.
assert(structVal->gtType == structType);
alreadyNormalized = true;
@@ -1771,10 +1740,8 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
break;
case GT_CNS_VEC:
- {
assert(varTypeIsSIMD(structVal) && (structVal->gtType == structType));
break;
- }
#ifdef FEATURE_SIMD
case GT_SIMD:
@@ -1817,7 +1784,7 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
#ifdef FEATURE_SIMD
if (blockNode->OperIsSimdOrHWintrinsic() || blockNode->IsCnsVec())
{
- parent->AsOp()->gtOp2 = impNormStructVal(blockNode, structHnd, curLevel, forceNormalization);
+ parent->AsOp()->gtOp2 = impNormStructVal(blockNode, structHnd, curLevel);
alreadyNormalized = true;
}
else
@@ -1852,7 +1819,7 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
}
structVal->gtType = structType;
- if (!alreadyNormalized || forceNormalization)
+ if (!alreadyNormalized)
{
if (makeTemp)
{
@@ -1865,7 +1832,7 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal,
structLcl = gtNewLclvNode(tmpNum, structType)->AsLclVarCommon();
structVal = structLcl;
}
- if ((forceNormalization || (structType == TYP_STRUCT)) && !structVal->OperIsBlk())
+ if ((structType == TYP_STRUCT) && !structVal->OperIsBlk())
{
// Wrap it in a GT_OBJ
structVal = gtNewObjNode(structHnd, gtNewOperNode(GT_ADDR, TYP_BYREF, structVal));
@@ -1938,8 +1905,8 @@ GenTree* Compiler::impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken,
}
// Generate the full lookup tree. May be null if we're abandoning an inline attempt.
- GenTree* result = impLookupToTree(pResolvedToken, &embedInfo.lookup, gtTokenToIconFlags(pResolvedToken->token),
- embedInfo.compileTimeHandle);
+ GenTreeFlags handleType = importParent ? GTF_ICON_CLASS_HDL : gtTokenToIconFlags(pResolvedToken->token);
+ GenTree* result = impLookupToTree(pResolvedToken, &embedInfo.lookup, handleType, embedInfo.compileTimeHandle);
// If we have a result and it requires runtime lookup, wrap it in a runtime lookup node.
if ((result != nullptr) && embedInfo.lookup.lookupKind.needsRuntimeLookup)
@@ -8853,9 +8820,7 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT
// Create the address node.
GenTreeFlags handleKind = isBoxedStatic ? GTF_ICON_STATIC_BOX_PTR : GTF_ICON_STATIC_HDL;
op1 = gtNewIconHandleNode((size_t)fldAddr, handleKind, innerFldSeq);
-#ifdef DEBUG
- op1->AsIntCon()->gtTargetHandle = op1->AsIntCon()->gtIconVal;
-#endif
+ INDEBUG(op1->AsIntCon()->gtTargetHandle = reinterpret_cast(pResolvedToken->hField));
if (pFieldInfo->fieldFlags & CORINFO_FLG_FIELD_INITCLASS)
{
@@ -10082,11 +10047,23 @@ var_types Compiler::impImportCall(OPCODE opcode,
call->gtFlags |= obj->gtFlags & GTF_GLOB_EFFECT;
call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(obj).WellKnown(WellKnownArg::ThisPointer));
- // Is this a virtual or interface call?
+ if (impIsThis(obj))
+ {
+ call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_NONVIRT_SAME_THIS;
+ }
+ }
+
+ bool probing;
+ probing = impConsiderCallProbe(call->AsCall(), rawILOffset);
+
+ // See if we can devirt if we aren't probing.
+ if (!probing && opts.OptimizationEnabled())
+ {
if (call->AsCall()->IsVirtual())
{
// only true object pointers can be virtual
- assert(obj->gtType == TYP_REF);
+ assert(call->AsCall()->gtArgs.HasThisPointer() &&
+ call->AsCall()->gtArgs.GetThisArg()->GetNode()->TypeIs(TYP_REF));
// See if we can devirtualize.
@@ -10102,10 +10079,10 @@ var_types Compiler::impImportCall(OPCODE opcode,
//
methHnd = callInfo->hMethod;
}
-
- if (impIsThis(obj))
+ else if (call->AsCall()->IsDelegateInvoke())
{
- call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_NONVIRT_SAME_THIS;
+ considerGuardedDevirtualization(call->AsCall(), rawILOffset, false, NO_METHOD_HANDLE, NO_CLASS_HANDLE,
+ nullptr);
}
}
@@ -10542,7 +10519,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
// important devirtualizations, we'll want to allow both a class probe and a captured context.
//
if (origCall->IsVirtual() && (origCall->gtCallType != CT_INDIRECT) && (exactContextHnd != nullptr) &&
- (origCall->gtClassProfileCandidateInfo == nullptr))
+ (origCall->gtHandleHistogramProfileCandidateInfo == nullptr))
{
JITDUMP("\nSaving context %p for call [%06u]\n", exactContextHnd, dspTreeID(origCall));
origCall->gtCallMoreFlags |= GTF_CALL_M_HAS_LATE_DEVIRT_INFO;
@@ -12152,38 +12129,31 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
// Check if this cast helper have some profile data
if (impIsCastHelperMayHaveProfileData(helper))
{
- bool doRandomDevirt = false;
- const int maxLikelyClasses = 32;
- int likelyClassCount = 0;
- LikelyClassRecord likelyClasses[maxLikelyClasses];
-#ifdef DEBUG
- // Optional stress mode to pick a random known class, rather than
- // the most likely known class.
- doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0;
+ const int maxLikelyClasses = 32;
+ LikelyClassMethodRecord likelyClasses[maxLikelyClasses];
+ unsigned likelyClassCount =
+ getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset);
- if (doRandomDevirt)
+ if (likelyClassCount > 0)
{
- // Reuse the random inliner's random state.
- CLRRandom* const random =
- impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization());
- likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random);
- likelyClasses[0].likelihood = 100;
- if (likelyClasses[0].clsHandle != NO_CLASS_HANDLE)
+#ifdef DEBUG
+ // Optional stress mode to pick a random known class, rather than
+ // the most likely known class.
+ if (JitConfig.JitRandomGuardedDevirtualization() != 0)
{
- likelyClassCount = 1;
+ // Reuse the random inliner's random state.
+ CLRRandom* const random =
+ impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization());
+
+ unsigned index = static_cast(random->Next(static_cast(likelyClassCount)));
+ likelyClasses[0].handle = likelyClasses[index].handle;
+ likelyClasses[0].likelihood = 100;
+ likelyClassCount = 1;
}
- }
- else
#endif
- {
- likelyClassCount = getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount,
- fgPgoData, ilOffset);
- }
- if (likelyClassCount > 0)
- {
- LikelyClassRecord likelyClass = likelyClasses[0];
- CORINFO_CLASS_HANDLE likelyCls = likelyClass.clsHandle;
+ LikelyClassMethodRecord likelyClass = likelyClasses[0];
+ CORINFO_CLASS_HANDLE likelyCls = (CORINFO_CLASS_HANDLE)likelyClass.handle;
if ((likelyCls != NO_CLASS_HANDLE) &&
(likelyClass.likelihood > (UINT32)JitConfig.JitGuardedDevirtualizationChainLikelihood()))
@@ -12218,13 +12188,14 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
op2->gtFlags |= GTF_DONT_CSE;
GenTreeCall* call = gtNewHelperCallNode(helper, TYP_REF, op2, op1);
- if (impIsCastHelperEligibleForClassProbe(call) && !impIsClassExact(pResolvedToken->hClass))
+ if ((JitConfig.JitClassProfiling() > 0) && impIsCastHelperEligibleForClassProbe(call) &&
+ !impIsClassExact(pResolvedToken->hClass))
{
- ClassProfileCandidateInfo* pInfo = new (this, CMK_Inlining) ClassProfileCandidateInfo;
- pInfo->ilOffset = ilOffset;
- pInfo->probeIndex = info.compClassProbeCount++;
- call->gtClassProfileCandidateInfo = pInfo;
- compCurBB->bbFlags |= BBF_HAS_CLASS_PROFILE;
+ HandleHistogramProfileCandidateInfo* pInfo = new (this, CMK_Inlining) HandleHistogramProfileCandidateInfo;
+ pInfo->ilOffset = ilOffset;
+ pInfo->probeIndex = info.compHandleHistogramProbeCount++;
+ call->gtHandleHistogramProfileCandidateInfo = pInfo;
+ compCurBB->bbFlags |= BBF_HAS_HISTOGRAM_PROFILE;
}
return call;
}
@@ -19907,8 +19878,8 @@ void Compiler::impCheckCanInline(GenTreeCall* call,
if (pParam->result->IsFailure())
{
- // Make sure not to report this one. It was already reported by the VM.
- pParam->result->SetReported();
+ // Do not report this as a failure. Instead report as a "VM failure"
+ pParam->result->SetVMFailure();
goto _exit;
}
@@ -21016,7 +20987,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
// Delegate Invoke method doesn't have a body and gets special cased instead.
// Don't even bother trying to inline it.
- if (call->IsDelegateInvoke())
+ if (call->IsDelegateInvoke() && !call->IsGuardedDevirtualizationCandidate())
{
inlineResult.NoteFatal(InlineObservation::CALLEE_HAS_NO_BODY);
return;
@@ -21201,7 +21172,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
// Since we're not actually inlining yet, and this call site is
// still just an inline candidate, there's nothing to report.
- inlineResult.SetReported();
+ inlineResult.SetSuccessResult(INLINE_CHECK_CAN_INLINE_SUCCESS);
}
/******************************************************************************/
@@ -21401,51 +21372,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
// This should be a virtual vtable or virtual stub call.
//
assert(call->IsVirtual());
-
- // Possibly instrument. Note for OSR+PGO we will instrument when
- // optimizing and (currently) won't devirtualize. We may want
- // to revisit -- if we can devirtualize we should be able to
- // suppress the probe.
- //
- // We strip BBINSTR from inlinees currently, so we'll only
- // do this for the root method calls.
- //
- if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR))
- {
- assert(opts.OptimizationDisabled() || opts.IsOSR());
- assert(!compIsForInlining());
-
- // During importation, optionally flag this block as one that
- // contains calls requiring class profiling. Ideally perhaps
- // we'd just keep track of the calls themselves, so we don't
- // have to search for them later.
- //
- if ((call->gtCallType != CT_INDIRECT) && opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) &&
- !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) && (JitConfig.JitClassProfiling() > 0) &&
- !isLateDevirtualization)
- {
- JITDUMP("\n ... marking [%06u] in " FMT_BB " for class profile instrumentation\n", dspTreeID(call),
- compCurBB->bbNum);
- ClassProfileCandidateInfo* pInfo = new (this, CMK_Inlining) ClassProfileCandidateInfo;
-
- // Record some info needed for the class profiling probe.
- //
- pInfo->ilOffset = ilOffset;
- pInfo->probeIndex = info.compClassProbeCount++;
- call->gtClassProfileCandidateInfo = pInfo;
-
- // Flag block as needing scrutiny
- //
- compCurBB->bbFlags |= BBF_HAS_CLASS_PROFILE;
- }
- return;
- }
-
- // Bail if optimizations are disabled.
- if (opts.OptimizationDisabled())
- {
- return;
- }
+ assert(opts.OptimizationEnabled());
#if defined(DEBUG)
// Bail if devirt is disabled.
@@ -21537,8 +21464,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return;
}
- considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass,
- pContextHandle DEBUGARG(objClass) DEBUGARG("unknown"));
+ considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle);
return;
}
@@ -21588,8 +21514,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return;
}
- considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass,
- pContextHandle DEBUGARG(objClass) DEBUGARG(objClassName));
+ considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle);
return;
}
@@ -21705,8 +21630,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
return;
}
- considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass,
- pContextHandle DEBUGARG(objClass) DEBUGARG(objClassName));
+ considerGuardedDevirtualization(call, ilOffset, isInterface, baseMethod, baseClass, pContextHandle);
return;
}
@@ -21726,6 +21650,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
call->gtCallMethHnd = derivedMethod;
call->gtCallType = CT_USER_FUNC;
+ call->gtControlExpr = nullptr;
call->gtCallMoreFlags |= GTF_CALL_M_DEVIRTUALIZED;
// Virtual calls include an implicit null check, which we may
@@ -21767,14 +21692,14 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
if (JitConfig.JitCrossCheckDevirtualizationAndPGO() && canSensiblyCheck)
{
// We only can handle a single likely class for now
- const int maxLikelyClasses = 1;
- LikelyClassRecord likelyClasses[maxLikelyClasses];
+ const int maxLikelyClasses = 1;
+ LikelyClassMethodRecord likelyClasses[maxLikelyClasses];
UINT32 numberOfClasses =
getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset);
UINT32 likelihood = likelyClasses[0].likelihood;
- CORINFO_CLASS_HANDLE likelyClass = likelyClasses[0].clsHandle;
+ CORINFO_CLASS_HANDLE likelyClass = (CORINFO_CLASS_HANDLE)likelyClasses[0].handle;
if (numberOfClasses > 0)
{
@@ -22065,6 +21990,117 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
#endif // FEATURE_READYTORUN
}
+//------------------------------------------------------------------------
+// impConsiderCallProbe: Consider whether a call should get a histogram probe
+// and mark it if so.
+//
+// Arguments:
+// call - The call
+// ilOffset - The precise IL offset of the call
+//
+// Returns:
+// True if the call was marked such that we will add a class or method probe for it.
+//
+bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset)
+{
+ // Possibly instrument. Note for OSR+PGO we will instrument when
+ // optimizing and (currently) won't devirtualize. We may want
+ // to revisit -- if we can devirtualize we should be able to
+ // suppress the probe.
+ //
+ // We strip BBINSTR from inlinees currently, so we'll only
+ // do this for the root method calls.
+ //
+ if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR))
+ {
+ return false;
+ }
+
+ assert(opts.OptimizationDisabled() || opts.IsOSR());
+ assert(!compIsForInlining());
+
+ // During importation, optionally flag this block as one that
+ // contains calls requiring class profiling. Ideally perhaps
+ // we'd just keep track of the calls themselves, so we don't
+ // have to search for them later.
+ //
+ if (compClassifyGDVProbeType(call) == GDVProbeType::None)
+ {
+ return false;
+ }
+
+ JITDUMP("\n ... marking [%06u] in " FMT_BB " for method/class profile instrumentation\n", dspTreeID(call),
+ compCurBB->bbNum);
+ HandleHistogramProfileCandidateInfo* pInfo = new (this, CMK_Inlining) HandleHistogramProfileCandidateInfo;
+
+ // Record some info needed for the class profiling probe.
+ //
+ pInfo->ilOffset = ilOffset;
+ pInfo->probeIndex = info.compHandleHistogramProbeCount++;
+ call->gtHandleHistogramProfileCandidateInfo = pInfo;
+
+ // Flag block as needing scrutiny
+ //
+ compCurBB->bbFlags |= BBF_HAS_HISTOGRAM_PROFILE;
+ return true;
+}
+
+//------------------------------------------------------------------------
+// compClassifyGDVProbeType:
+// Classify the type of GDV probe to use for a call site.
+//
+// Arguments:
+// call - The call
+//
+// Returns:
+// The type of probe to use.
+//
+Compiler::GDVProbeType Compiler::compClassifyGDVProbeType(GenTreeCall* call)
+{
+ if (call->gtCallType == CT_INDIRECT)
+ {
+ return GDVProbeType::None;
+ }
+
+ if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) || opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
+ {
+ return GDVProbeType::None;
+ }
+
+ bool createTypeHistogram = false;
+ if (JitConfig.JitClassProfiling() > 0)
+ {
+ createTypeHistogram = call->IsVirtualStub() || call->IsVirtualVtable();
+
+ // Cast helpers may conditionally (depending on whether the class is
+ // exact or not) have probes. For those helpers we do not use this
+ // function to classify the probe type until after we have decided on
+ // whether we probe them or not.
+ createTypeHistogram = createTypeHistogram || (impIsCastHelperEligibleForClassProbe(call) &&
+ (call->gtHandleHistogramProfileCandidateInfo != nullptr));
+ }
+
+ bool createMethodHistogram = ((JitConfig.JitDelegateProfiling() > 0) && call->IsDelegateInvoke()) ||
+ ((JitConfig.JitVTableProfiling() > 0) && call->IsVirtualVtable());
+
+ if (createTypeHistogram && createMethodHistogram)
+ {
+ return GDVProbeType::MethodAndClassProfile;
+ }
+
+ if (createTypeHistogram)
+ {
+ return GDVProbeType::ClassProfile;
+ }
+
+ if (createMethodHistogram)
+ {
+ return GDVProbeType::MethodProfile;
+ }
+
+ return GDVProbeType::None;
+}
+
//------------------------------------------------------------------------
// impGetSpecialIntrinsicExactReturnType: Look for special cases where a call
// to an intrinsic returns an exact type
@@ -22075,7 +22111,6 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
// Returns:
// Exact class handle returned by the intrinsic call, if known.
// Nullptr if not known, or not likely to lead to beneficial optimization.
-
CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_METHOD_HANDLE methodHnd)
{
JITDUMP("Special intrinsic: looking for exact type returned by %s\n", eeGetMethodFullName(methodHnd));
@@ -22237,153 +22272,380 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call)
}
//------------------------------------------------------------------------
-// considerGuardedDevirtualization: see if we can profitably guess at the
-// class involved in an interface or virtual call.
+// pickGDV: Use profile information to pick a GDV candidate for a call site.
//
// Arguments:
+// call - the call
+// ilOffset - exact IL offset of the call
+// isInterface - whether or not the call target is defined on an interface
+// classGuess - [out] the class to guess for (mutually exclusive with methodGuess)
+// methodGuess - [out] the method to guess for (mutually exclusive with classGuess)
+// likelihood - [out] an estimate of the likelihood that the guess will succeed
//
-// call - potential guarded devirtualization candidate
-// ilOffset - IL ofset of the call instruction
-// isInterface - true if this is an interface call
-// baseMethod - target method of the call
-// baseClass - class that introduced the target method
-// pContextHandle - context handle for the call
-// objClass - class of 'this' in the call
-// objClassName - name of the obj Class
-//
-// Notes:
-// Consults with VM to see if there's a likely class at runtime,
-// if so, adds a candidate for guarded devirtualization.
-//
-void Compiler::considerGuardedDevirtualization(
- GenTreeCall* call,
- IL_OFFSET ilOffset,
- bool isInterface,
- CORINFO_METHOD_HANDLE baseMethod,
- CORINFO_CLASS_HANDLE baseClass,
- CORINFO_CONTEXT_HANDLE* pContextHandle DEBUGARG(CORINFO_CLASS_HANDLE objClass) DEBUGARG(const char* objClassName))
+void Compiler::pickGDV(GenTreeCall* call,
+ IL_OFFSET ilOffset,
+ bool isInterface,
+ CORINFO_CLASS_HANDLE* classGuess,
+ CORINFO_METHOD_HANDLE* methodGuess,
+ unsigned* likelihood)
{
-#if defined(DEBUG)
- const char* callKind = isInterface ? "interface" : "virtual";
-#endif
+ *classGuess = NO_CLASS_HANDLE;
+ *methodGuess = NO_METHOD_HANDLE;
+ *likelihood = 0;
- JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset);
+ const int maxLikelyClasses = 32;
+ LikelyClassMethodRecord likelyClasses[maxLikelyClasses];
+ unsigned numberOfClasses = 0;
+ if (call->IsVirtualStub() || call->IsVirtualVtable())
+ {
+ numberOfClasses =
+ getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset);
+ }
- // We currently only get likely class guesses when there is PGO data
- // with class profiles.
+ const int maxLikelyMethods = 32;
+ LikelyClassMethodRecord likelyMethods[maxLikelyMethods];
+ unsigned numberOfMethods = 0;
+
+ // TODO-GDV: R2R support requires additional work to reacquire the
+ // entrypoint, similar to what happens at the end of impDevirtualizeCall.
+ // As part of supporting this we should merge the tail of
+ // impDevirtualizeCall and what happens in
+ // GuardedDevirtualizationTransformer::CreateThen for method GDV.
//
- if (fgPgoClassProfiles == 0)
+ if (!opts.IsReadyToRun() && (call->IsVirtualVtable() || call->IsDelegateInvoke()))
{
- JITDUMP("Not guessing for class: no class profile pgo data, or pgo disabled\n");
+ numberOfMethods =
+ getLikelyMethods(likelyMethods, maxLikelyMethods, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset);
+ }
+
+ if ((numberOfClasses < 1) && (numberOfMethods < 1))
+ {
+ JITDUMP("No likely class or method, sorry\n");
return;
}
- // See if there's a likely guess for the class.
- //
- const unsigned likelihoodThreshold = isInterface ? 25 : 30;
- unsigned likelihood = 0;
- unsigned numberOfClasses = 0;
+#ifdef DEBUG
+ if ((verbose || JitConfig.EnableExtraSuperPmiQueries()) && (numberOfClasses > 0))
+ {
+ bool isExact;
+ bool isNonNull;
+ CallArg* thisArg = call->gtArgs.GetThisArg();
+ CORINFO_CLASS_HANDLE declaredThisClsHnd = gtGetClassHandle(thisArg->GetNode(), &isExact, &isNonNull);
+ JITDUMP("Likely classes for call [%06u]", dspTreeID(call));
+ if (declaredThisClsHnd != NO_CLASS_HANDLE)
+ {
+ const char* baseClassName = eeGetClassName(declaredThisClsHnd);
+ JITDUMP(" on class %p (%s)", declaredThisClsHnd, baseClassName);
+ }
+ JITDUMP("\n");
- CORINFO_CLASS_HANDLE likelyClass = NO_CLASS_HANDLE;
+ for (UINT32 i = 0; i < numberOfClasses; i++)
+ {
+ const char* className = eeGetClassName((CORINFO_CLASS_HANDLE)likelyClasses[i].handle);
+ JITDUMP(" %u) %p (%s) [likelihood:%u%%]\n", i + 1, likelyClasses[i].handle, className,
+ likelyClasses[i].likelihood);
+ }
+ }
- bool doRandomDevirt = false;
+ if ((verbose || JitConfig.EnableExtraSuperPmiQueries()) && (numberOfMethods > 0))
+ {
+ assert(call->gtCallType == CT_USER_FUNC);
+ const char* baseMethName = eeGetMethodFullName(call->gtCallMethHnd);
+ JITDUMP("Likely methods for call [%06u] to method %s\n", dspTreeID(call), baseMethName);
- const int maxLikelyClasses = 32;
- LikelyClassRecord likelyClasses[maxLikelyClasses];
+ for (UINT32 i = 0; i < numberOfMethods; i++)
+ {
+ CORINFO_CONST_LOOKUP lookup = {};
+ info.compCompHnd->getFunctionFixedEntryPoint((CORINFO_METHOD_HANDLE)likelyMethods[i].handle, false,
+ &lookup);
+
+ const char* methName = eeGetMethodFullName((CORINFO_METHOD_HANDLE)likelyMethods[i].handle);
+ switch (lookup.accessType)
+ {
+ case IAT_VALUE:
+ JITDUMP(" %u) %p (%s) [likelihood:%u%%]\n", i + 1, lookup.addr, methName,
+ likelyMethods[i].likelihood);
+ break;
+ case IAT_PVALUE:
+ JITDUMP(" %u) [%p] (%s) [likelihood:%u%%]\n", i + 1, lookup.addr, methName,
+ likelyMethods[i].likelihood);
+ break;
+ case IAT_PPVALUE:
+ JITDUMP(" %u) [[%p]] (%s) [likelihood:%u%%]\n", i + 1, lookup.addr, methName,
+ likelyMethods[i].likelihood);
+ break;
+ default:
+ JITDUMP(" %u) %s [likelihood:%u%%]\n", i + 1, methName, likelyMethods[i].likelihood);
+ break;
+ }
+ }
+ }
-#ifdef DEBUG
// Optional stress mode to pick a random known class, rather than
// the most likely known class.
//
- doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0;
-
- if (doRandomDevirt)
+ if (JitConfig.JitRandomGuardedDevirtualization() != 0)
{
// Reuse the random inliner's random state.
//
CLRRandom* const random =
impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization());
- likelyClasses[0].clsHandle = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random);
- likelyClasses[0].likelihood = 100;
- if (likelyClasses[0].clsHandle != NO_CLASS_HANDLE)
+ unsigned index = static_cast(random->Next(static_cast(numberOfClasses + numberOfMethods)));
+ if (index < numberOfClasses)
{
- numberOfClasses = 1;
+ *classGuess = (CORINFO_CLASS_HANDLE)likelyClasses[index].handle;
+ *likelihood = 100;
+ JITDUMP("Picked random class for GDV: %p (%s)\n", *classGuess, eeGetClassName(*classGuess));
+ return;
+ }
+ else
+ {
+ *methodGuess = (CORINFO_METHOD_HANDLE)likelyMethods[index - numberOfClasses].handle;
+ *likelihood = 100;
+ JITDUMP("Picked random method for GDV: %p (%s)\n", *methodGuess, eeGetMethodFullName(*methodGuess));
+ return;
}
}
- else
#endif
+
+ // Prefer class guess as it is cheaper
+ if (numberOfClasses > 0)
{
- numberOfClasses =
- getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset);
+ unsigned likelihoodThreshold = isInterface ? 25 : 30;
+ if (likelyClasses[0].likelihood >= likelihoodThreshold)
+ {
+ *classGuess = (CORINFO_CLASS_HANDLE)likelyClasses[0].handle;
+ *likelihood = likelyClasses[0].likelihood;
+ return;
+ }
+
+ JITDUMP("Not guessing for class; likelihood is below %s call threshold %u\n",
+ isInterface ? "interface" : "virtual", likelihoodThreshold);
}
- // For now we only use the most popular type
+ if (numberOfMethods > 0)
+ {
+ unsigned likelihoodThreshold = 30;
+ if (likelyMethods[0].likelihood >= likelihoodThreshold)
+ {
+ *methodGuess = (CORINFO_METHOD_HANDLE)likelyMethods[0].handle;
+ *likelihood = likelyMethods[0].likelihood;
+ return;
+ }
- likelihood = likelyClasses[0].likelihood;
- likelyClass = likelyClasses[0].clsHandle;
+ JITDUMP("Not guessing for method; likelihood is below %s call threshold %u\n",
+ call->IsDelegateInvoke() ? "delegate" : "virtual", likelihoodThreshold);
+ }
+}
- if (numberOfClasses < 1)
+//------------------------------------------------------------------------
+// isCompatibleMethodGDV:
+// Check if devirtualizing a call node as a specified target method call is
+// reasonable.
+//
+// Arguments:
+// call - the call
+// gdvTarget - the target method that we want to guess for and devirtualize to
+//
+// Returns:
+// true if we can proceed with GDV.
+//
+// Notes:
+// This implements a small simplified signature-compatibility check to
+// verify that a guess is reasonable. The main goal here is to avoid blowing
+// up the JIT on PGO data with stale GDV candidates; if they are not
+// compatible in the ECMA sense then we do not expect the guard to ever pass
+// at runtime, so we can get by with simplified rules here.
+//
+bool Compiler::isCompatibleMethodGDV(GenTreeCall* call, CORINFO_METHOD_HANDLE gdvTarget)
+{
+ CORINFO_SIG_INFO sig;
+ info.compCompHnd->getMethodSig(gdvTarget, &sig);
+
+ CORINFO_ARG_LIST_HANDLE sigParam = sig.args;
+ unsigned numParams = sig.numArgs;
+ unsigned numArgs = 0;
+ for (CallArg& arg : call->gtArgs.Args())
{
- JITDUMP("No likely class, sorry\n");
- return;
- }
+ switch (arg.GetWellKnownArg())
+ {
+ case WellKnownArg::RetBuffer:
+ case WellKnownArg::ThisPointer:
+ // Not part of signature but we still expect to see it here
+ continue;
+ case WellKnownArg::None:
+ break;
+ default:
+ assert(!"Unexpected well known arg to method GDV candidate");
+ continue;
+ }
+
+ numArgs++;
+ if (numArgs > numParams)
+ {
+ JITDUMP("Incompatible method GDV: call [%06u] has more arguments than signature (sig has %d parameters)\n",
+ dspTreeID(call), numParams);
+ return false;
+ }
+
+ CORINFO_CLASS_HANDLE classHnd = NO_CLASS_HANDLE;
+ CorInfoType corType = strip(info.compCompHnd->getArgType(&sig, sigParam, &classHnd));
+ var_types sigType = JITtype2varType(corType);
+
+ if (!impCheckImplicitArgumentCoercion(sigType, arg.GetNode()->TypeGet()))
+ {
+ JITDUMP("Incompatible method GDV: arg [%06u] is type-incompatible with signature of target\n",
+ dspTreeID(arg.GetNode()));
+ return false;
+ }
- assert(likelyClass != NO_CLASS_HANDLE);
+ // Best-effort check for struct compatibility here.
+ if (varTypeIsStruct(sigType) && (arg.GetSignatureClassHandle() != classHnd))
+ {
+ ClassLayout* callLayout = typGetObjLayout(arg.GetSignatureClassHandle());
+ ClassLayout* tarLayout = typGetObjLayout(classHnd);
- // Print all likely classes
- JITDUMP("%s classes for %p (%s):\n", doRandomDevirt ? "Random" : "Likely", dspPtr(objClass), objClassName)
- for (UINT32 i = 0; i < numberOfClasses; i++)
+ if (!ClassLayout::AreCompatible(callLayout, tarLayout))
+ {
+ JITDUMP("Incompatible method GDV: struct arg [%06u] is layout-incompatible with signature of target\n",
+ dspTreeID(arg.GetNode()));
+ return false;
+ }
+ }
+
+ sigParam = info.compCompHnd->getArgNext(sigParam);
+ }
+
+ if (numArgs < numParams)
{
- JITDUMP(" %u) %p (%s) [likelihood:%u%%]\n", i + 1, likelyClasses[i].clsHandle,
- eeGetClassName(likelyClasses[i].clsHandle), likelyClasses[i].likelihood);
+ JITDUMP("Incompatible method GDV: call [%06u] has fewer arguments (%d) than signature (%d)\n", dspTreeID(call),
+ numArgs, numParams);
+ return false;
}
- // Todo: a more advanced heuristic using likelihood, number of
- // classes, and the profile count for this block.
- //
- // For now we will guess if the likelihood is at least 25%/30% (intfc/virt), as studies
- // have shown this transformation should pay off even if we guess wrong sometimes.
+ return true;
+}
+
+//------------------------------------------------------------------------
+// considerGuardedDevirtualization: see if we can profitably guess at the
+// class involved in an interface or virtual call.
+//
+// Arguments:
+//
+// call - potential guarded devirtualization candidate
+// ilOffset - IL ofset of the call instruction
+// baseMethod - target method of the call
+// baseClass - class that introduced the target method
+// pContextHandle - context handle for the call
+//
+// Notes:
+// Consults with VM to see if there's a likely class at runtime,
+// if so, adds a candidate for guarded devirtualization.
+//
+void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
+ IL_OFFSET ilOffset,
+ bool isInterface,
+ CORINFO_METHOD_HANDLE baseMethod,
+ CORINFO_CLASS_HANDLE baseClass,
+ CORINFO_CONTEXT_HANDLE* pContextHandle)
+{
+ JITDUMP("Considering guarded devirtualization at IL offset %u (0x%x)\n", ilOffset, ilOffset);
+
+ // We currently only get likely class guesses when there is PGO data
+ // with class profiles.
//
- if (likelihood < likelihoodThreshold)
+ if ((fgPgoClassProfiles == 0) && (fgPgoMethodProfiles == 0))
{
- JITDUMP("Not guessing for class; likelihood is below %s call threshold %u\n", callKind, likelihoodThreshold);
+ JITDUMP("Not guessing for class or method: no GDV profile pgo data, or pgo disabled\n");
return;
}
- uint32_t const likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
+ CORINFO_CLASS_HANDLE likelyClass;
+ CORINFO_METHOD_HANDLE likelyMethod;
+ unsigned likelihood;
+ pickGDV(call, ilOffset, isInterface, &likelyClass, &likelyMethod, &likelihood);
- if ((likelyClassAttribs & CORINFO_FLG_ABSTRACT) != 0)
+ if ((likelyClass == NO_CLASS_HANDLE) && (likelyMethod == NO_METHOD_HANDLE))
{
- // We may see an abstract likely class, if we have a stale profile.
- // No point guessing for this.
- //
- JITDUMP("Not guessing for class; abstract (stale profile)\n");
return;
}
- // Figure out which method will be called.
- //
- CORINFO_DEVIRTUALIZATION_INFO dvInfo;
- dvInfo.virtualMethod = baseMethod;
- dvInfo.objClass = likelyClass;
- dvInfo.context = *pContextHandle;
- dvInfo.exactContext = *pContextHandle;
- dvInfo.pResolvedTokenVirtualMethod = nullptr;
+ uint32_t likelyClassAttribs = 0;
+ if (likelyClass != NO_CLASS_HANDLE)
+ {
+ likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
- const bool canResolve = info.compCompHnd->resolveVirtualMethod(&dvInfo);
+ if ((likelyClassAttribs & CORINFO_FLG_ABSTRACT) != 0)
+ {
+ // We may see an abstract likely class, if we have a stale profile.
+ // No point guessing for this.
+ //
+ JITDUMP("Not guessing for class; abstract (stale profile)\n");
+ return;
+ }
- if (!canResolve)
+ // Figure out which method will be called.
+ //
+ CORINFO_DEVIRTUALIZATION_INFO dvInfo;
+ dvInfo.virtualMethod = baseMethod;
+ dvInfo.objClass = likelyClass;
+ dvInfo.context = *pContextHandle;
+ dvInfo.exactContext = *pContextHandle;
+ dvInfo.pResolvedTokenVirtualMethod = nullptr;
+
+ const bool canResolve = info.compCompHnd->resolveVirtualMethod(&dvInfo);
+
+ if (!canResolve)
+ {
+ JITDUMP("Can't figure out which method would be invoked, sorry\n");
+ return;
+ }
+
+ likelyMethod = dvInfo.devirtualizedMethod;
+ }
+
+ uint32_t likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
+
+ if (likelyClass == NO_CLASS_HANDLE)
{
- JITDUMP("Can't figure out which method would be invoked, sorry\n");
- return;
+ // For method GDV do a few more checks that we get for free in the
+ // resolve call above for class-based GDV.
+ if ((likelyMethodAttribs & CORINFO_FLG_STATIC) != 0)
+ {
+ assert(call->IsDelegateInvoke());
+ JITDUMP("Cannot currently handle devirtualizing static delegate calls, sorry\n");
+ return;
+ }
+
+ // Verify that the call target and args look reasonable so that the JIT
+ // does not blow up during inlining/call morphing.
+ //
+ // NOTE: Once we want to support devirtualization of delegate calls to
+ // static methods and remove the check above we will start failing here
+ // for delegates pointing to static methods that have the first arg
+ // bound. For example:
+ //
+ // public static void E(this C c) ...
+ // Action a = new C().E;
+ //
+ // The delegate instance looks exactly like one pointing to an instance
+ // method in this case and the call will have zero args while the
+ // signature has 1 arg.
+ //
+ if (!isCompatibleMethodGDV(call, likelyMethod))
+ {
+ JITDUMP("Target for method-based GDV is incompatible (stale profile?)\n");
+ assert((fgPgoSource != ICorJitInfo::PgoSource::Dynamic) && "Unexpected stale profile in dynamic PGO data");
+ return;
+ }
}
- CORINFO_METHOD_HANDLE likelyMethod = dvInfo.devirtualizedMethod;
- JITDUMP("%s call would invoke method %s\n", callKind, eeGetMethodName(likelyMethod, nullptr));
+ JITDUMP("%s call would invoke method %s\n",
+ isInterface ? "interface" : call->IsDelegateInvoke() ? "delegate" : "virtual",
+ eeGetMethodName(likelyMethod, nullptr));
// Add this as a potential candidate.
//
- uint32_t const likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs,
likelihood);
}
@@ -22416,8 +22678,8 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
unsigned classAttr,
unsigned likelihood)
{
- // This transformation only makes sense for virtual calls
- assert(call->IsVirtual());
+ // This transformation only makes sense for delegate and virtual calls
+ assert(call->IsDelegateInvoke() || call->IsVirtual());
// Only mark calls if the feature is enabled.
const bool isEnabled = JitConfig.JitEnableGuardedDevirtualization() > 0;
@@ -22467,8 +22729,9 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
// We're all set, proceed with candidate creation.
//
- JITDUMP("Marking call [%06u] as guarded devirtualization candidate; will guess for class %s\n", dspTreeID(call),
- eeGetClassName(classHandle));
+ JITDUMP("Marking call [%06u] as guarded devirtualization candidate; will guess for %s %s\n", dspTreeID(call),
+ classHandle != NO_CLASS_HANDLE ? "class" : "method",
+ classHandle != NO_CLASS_HANDLE ? eeGetClassName(classHandle) : eeGetMethodFullName(methodHandle));
setMethodHasGuardedDevirtualization();
call->SetGuardedDevirtualizationCandidate();
diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp
index 14242b8f718b90..e3d799f734b8ba 100644
--- a/src/coreclr/jit/indirectcalltransformer.cpp
+++ b/src/coreclr/jit/indirectcalltransformer.cpp
@@ -450,9 +450,11 @@ class IndirectCallTransformer
class GuardedDevirtualizationTransformer final : public Transformer
{
+ unsigned m_targetLclNum;
+
public:
GuardedDevirtualizationTransformer(Compiler* compiler, BasicBlock* block, Statement* stmt)
- : Transformer(compiler, block, stmt), returnTemp(BAD_VAR_NUM)
+ : Transformer(compiler, block, stmt), m_targetLclNum(BAD_VAR_NUM), returnTemp(BAD_VAR_NUM)
{
}
@@ -538,23 +540,26 @@ class IndirectCallTransformer
checkBlock = currBlock;
checkBlock->bbJumpKind = BBJ_COND;
- // Fetch method table from object arg to call.
- GenTree* thisTree = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetNode());
+ CallArg* thisArg = origCall->gtArgs.GetThisArg();
+ GenTree* thisTree = thisArg->GetNode();
// Create temp for this if the tree is costly.
- if (!thisTree->IsLocal())
+ if (thisTree->IsLocal())
+ {
+ thisTree = compiler->gtCloneExpr(thisTree);
+ }
+ else
{
const unsigned thisTempNum = compiler->lvaGrabTemp(true DEBUGARG("guarded devirt this temp"));
- // lvaSetClass(thisTempNum, ...);
- GenTree* asgTree = compiler->gtNewTempAssign(thisTempNum, thisTree);
- Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo());
+ GenTree* asgTree = compiler->gtNewTempAssign(thisTempNum, thisTree);
+ Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, asgStmt);
thisTree = compiler->gtNewLclvNode(thisTempNum, TYP_REF);
// Propagate the new this to the call. Must be a new expr as the call
// will live on in the else block and thisTree is used below.
- origCall->gtArgs.GetThisArg()->SetEarlyNode(compiler->gtNewLclvNode(thisTempNum, TYP_REF));
+ thisArg->SetEarlyNode(compiler->gtNewLclvNode(thisTempNum, TYP_REF));
}
// Remember the current last statement. If we're doing a chained GDV, we'll clone/copy
@@ -565,18 +570,96 @@ class IndirectCallTransformer
//
lastStmt = checkBlock->lastStmt();
- // Find target method table
- //
- GenTree* methodTable = compiler->gtNewMethodTableLookup(thisTree);
- GuardedDevirtualizationCandidateInfo* guardedInfo = origCall->gtGuardedDevirtualizationCandidateInfo;
- CORINFO_CLASS_HANDLE clsHnd = guardedInfo->guardedClassHandle;
- GenTree* targetMethodTable = compiler->gtNewIconEmbClsHndNode(clsHnd);
+ GuardedDevirtualizationCandidateInfo* guardedInfo = origCall->gtGuardedDevirtualizationCandidateInfo;
- // Compare and jump to else (which does the indirect call) if NOT equal
- //
- GenTree* methodTableCompare = compiler->gtNewOperNode(GT_NE, TYP_INT, targetMethodTable, methodTable);
- GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, methodTableCompare);
- Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
+ // Create comparison. On success we will jump to do the indirect call.
+ GenTree* compare;
+ if (guardedInfo->guardedClassHandle != NO_CLASS_HANDLE)
+ {
+ // Find target method table
+ //
+ GenTree* methodTable = compiler->gtNewMethodTableLookup(thisTree);
+ CORINFO_CLASS_HANDLE clsHnd = guardedInfo->guardedClassHandle;
+ GenTree* targetMethodTable = compiler->gtNewIconEmbClsHndNode(clsHnd);
+
+ compare = compiler->gtNewOperNode(GT_NE, TYP_INT, targetMethodTable, methodTable);
+ }
+ else
+ {
+ assert(origCall->IsVirtualVtable() || origCall->IsDelegateInvoke());
+ // We reuse the target except if this is a chained GDV, in
+ // which case the check will be moved into the success case of
+ // a previous GDV and thus may not execute when we hit the cold
+ // path.
+ // TODO-GDV: Consider duplicating the store at the end of the
+ // cold case for the previous GDV. Then we can reuse the target
+ // if the second check of a chained GDV fails.
+ bool reuseTarget = (origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_CHAIN) == 0;
+ if (origCall->IsVirtualVtable())
+ {
+ GenTree* tarTree = compiler->fgExpandVirtualVtableCallTarget(origCall);
+
+ if (reuseTarget)
+ {
+ m_targetLclNum = compiler->lvaGrabTemp(false DEBUGARG("guarded devirt call target temp"));
+
+ GenTree* asgTree = compiler->gtNewTempAssign(m_targetLclNum, tarTree);
+ Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo());
+ compiler->fgInsertStmtAtEnd(checkBlock, asgStmt);
+
+ tarTree = compiler->gtNewLclvNode(m_targetLclNum, TYP_I_IMPL);
+ }
+
+ CORINFO_METHOD_HANDLE methHnd = guardedInfo->guardedMethodHandle;
+ CORINFO_CONST_LOOKUP lookup;
+ compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &lookup);
+
+ GenTree* compareTarTree = CreateTreeForLookup(methHnd, lookup);
+ compare = compiler->gtNewOperNode(GT_NE, TYP_INT, compareTarTree, tarTree);
+ }
+ else
+ {
+ // Reusing the call target for delegates is more
+ // complicated. Essentially we need to do the
+ // transformation done in LowerDelegateInvoke by converting
+ // the call to CT_INDIRECT and reusing the target address.
+ // We will do that transformation in CreateElse, but here
+ // we need to stash the target.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef TARGET_ARM
+ // Not impossible to support, but would additionally
+ // require us to load the wrapper delegate cell when
+ // expanding.
+ reuseTarget &= (origCall->gtCallMoreFlags & GTF_CALL_M_WRAPPER_DELEGATE_INV) == 0;
+#endif
+
+ GenTree* offset =
+ compiler->gtNewIconNode((ssize_t)compiler->eeGetEEInfo()->offsetOfDelegateFirstTarget,
+ TYP_I_IMPL);
+ GenTree* tarTree = compiler->gtNewOperNode(GT_ADD, TYP_BYREF, thisTree, offset);
+ tarTree = compiler->gtNewIndir(TYP_I_IMPL, tarTree);
+
+ if (reuseTarget)
+ {
+ m_targetLclNum = compiler->lvaGrabTemp(false DEBUGARG("guarded devirt call target temp"));
+
+ GenTree* asgTree = compiler->gtNewTempAssign(m_targetLclNum, tarTree);
+ Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo());
+ compiler->fgInsertStmtAtEnd(checkBlock, asgStmt);
+ tarTree = compiler->gtNewLclvNode(m_targetLclNum, TYP_I_IMPL);
+ }
+
+ CORINFO_METHOD_HANDLE methHnd = guardedInfo->guardedMethodHandle;
+ CORINFO_CONST_LOOKUP lookup;
+ compiler->info.compCompHnd->getFunctionFixedEntryPoint(methHnd, false, &lookup);
+
+ GenTree* compareTarTree = CreateTreeForLookup(methHnd, lookup);
+ compare = compiler->gtNewOperNode(GT_NE, TYP_INT, compareTarTree, tarTree);
+ }
+ }
+
+ GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, compare);
+ Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt);
}
@@ -682,35 +765,94 @@ class IndirectCallTransformer
InlineCandidateInfo* inlineInfo = origCall->gtInlineCandidateInfo;
CORINFO_CLASS_HANDLE clsHnd = inlineInfo->guardedClassHandle;
- // copy 'this' to temp with exact type.
+ //
+ // Copy the 'this' for the devirtualized call to a new temp. For
+ // class-based GDV this will allow us to set the exact type on that
+ // temp. For delegate GDV, this will be the actual 'this' object
+ // stored in the delegate.
+ //
const unsigned thisTemp = compiler->lvaGrabTemp(false DEBUGARG("guarded devirt this exact temp"));
GenTree* clonedObj = compiler->gtCloneExpr(origCall->gtArgs.GetThisArg()->GetNode());
- GenTree* assign = compiler->gtNewTempAssign(thisTemp, clonedObj);
- compiler->lvaSetClass(thisTemp, clsHnd, true);
+ GenTree* newThisObj;
+ if (origCall->IsDelegateInvoke())
+ {
+ GenTree* offset =
+ compiler->gtNewIconNode((ssize_t)compiler->eeGetEEInfo()->offsetOfDelegateInstance, TYP_I_IMPL);
+ newThisObj = compiler->gtNewOperNode(GT_ADD, TYP_BYREF, clonedObj, offset);
+ newThisObj = compiler->gtNewIndir(TYP_REF, newThisObj);
+ }
+ else
+ {
+ newThisObj = clonedObj;
+ }
+ GenTree* assign = compiler->gtNewTempAssign(thisTemp, newThisObj);
+
+ if (clsHnd != NO_CLASS_HANDLE)
+ {
+ compiler->lvaSetClass(thisTemp, clsHnd, true);
+ }
+ else
+ {
+ compiler->lvaSetClass(thisTemp,
+ compiler->info.compCompHnd->getMethodClass(inlineInfo->guardedMethodHandle));
+ }
+
compiler->fgNewStmtAtEnd(thenBlock, assign);
- // Clone call. Note we must use the special candidate helper.
+ // Clone call for the devirtualized case. Note we must use the
+ // special candidate helper and we need to use the new 'this'.
GenTreeCall* call = compiler->gtCloneCandidateCall(origCall);
call->gtArgs.GetThisArg()->SetEarlyNode(compiler->gtNewLclvNode(thisTemp, TYP_REF));
call->SetIsGuarded();
JITDUMP("Direct call [%06u] in block " FMT_BB "\n", compiler->dspTreeID(call), thenBlock->bbNum);
- // Then invoke impDevirtualizeCall to actually transform the call for us,
- // given the original (base) method and the exact guarded class. It should succeed.
- //
- CORINFO_METHOD_HANDLE methodHnd = call->gtCallMethHnd;
- unsigned methodFlags = compiler->info.compCompHnd->getMethodAttribs(methodHnd);
- CORINFO_CONTEXT_HANDLE context = inlineInfo->exactContextHnd;
- const bool isLateDevirtualization = true;
- const bool explicitTailCall = (call->AsCall()->gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL) != 0;
- compiler->impDevirtualizeCall(call, nullptr, &methodHnd, &methodFlags, &context, nullptr,
- isLateDevirtualization, explicitTailCall);
+ CORINFO_METHOD_HANDLE methodHnd = call->gtCallMethHnd;
+ CORINFO_CONTEXT_HANDLE context = inlineInfo->exactContextHnd;
+ if (clsHnd != NO_CLASS_HANDLE)
+ {
+ // Then invoke impDevirtualizeCall to actually transform the call for us,
+ // given the original (base) method and the exact guarded class. It should succeed.
+ //
+ unsigned methodFlags = compiler->info.compCompHnd->getMethodAttribs(methodHnd);
+ const bool isLateDevirtualization = true;
+ const bool explicitTailCall = (call->AsCall()->gtCallMoreFlags & GTF_CALL_M_EXPLICIT_TAILCALL) != 0;
+ compiler->impDevirtualizeCall(call, nullptr, &methodHnd, &methodFlags, &context, nullptr,
+ isLateDevirtualization, explicitTailCall);
+ }
+ else
+ {
+ // Otherwise we know the exact method already, so just change
+ // the call as necessary here.
+ call->gtFlags &= ~GTF_CALL_VIRT_KIND_MASK;
+ call->gtCallMethHnd = methodHnd = inlineInfo->guardedMethodHandle;
+ call->gtCallType = CT_USER_FUNC;
+ call->gtCallMoreFlags |= GTF_CALL_M_DEVIRTUALIZED;
+ call->gtCallMoreFlags &= ~GTF_CALL_M_DELEGATE_INV;
+ // TODO-GDV: To support R2R we need to get the entry point
+ // here. We should unify with the tail of impDevirtualizeCall.
+
+ if (origCall->IsVirtual())
+ {
+ // Virtual calls include an implicit null check, which we may
+ // now need to make explicit.
+ bool isExact;
+ bool objIsNonNull;
+ compiler->gtGetClassHandle(newThisObj, &isExact, &objIsNonNull);
+
+ if (!objIsNonNull)
+ {
+ call->gtFlags |= GTF_CALL_NULLCHECK;
+ }
+ }
+
+ context = MAKE_METHODCONTEXT(methodHnd);
+ }
// We know this call can devirtualize or we would not have set up GDV here.
- // So impDevirtualizeCall should succeed in devirtualizing.
+ // So above code should succeed in devirtualizing.
//
- assert(!call->IsVirtual());
+ assert(!call->IsVirtual() && !call->IsDelegateInvoke());
// If the devirtualizer was unable to transform the call to invoke the unboxed entry, the inline info
// we set up may be invalid. We won't be able to inline anyways. So demote the call as an inline candidate.
@@ -776,7 +918,7 @@ class IndirectCallTransformer
}
//------------------------------------------------------------------------
- // CreateElse: create else block. This executes the unaltered indirect call.
+ // CreateElse: create else block. This executes the original indirect call.
//
virtual void CreateElse()
{
@@ -796,6 +938,38 @@ class IndirectCallTransformer
newStmt->SetRootNode(assign);
}
+ if (m_targetLclNum != BAD_VAR_NUM)
+ {
+ if (call->IsVirtualVtable())
+ {
+ // We already loaded the target once for the check, so reuse it from the temp.
+ call->gtControlExpr = compiler->gtNewLclvNode(m_targetLclNum, TYP_I_IMPL);
+ call->SetExpandedEarly();
+ }
+ else if (call->IsDelegateInvoke())
+ {
+ // Target was saved into a temp during check. We expand the
+ // delegate call to a CT_INDIRECT call that uses the target
+ // directly, somewhat similarly to LowerDelegateInvoke.
+ call->gtCallType = CT_INDIRECT;
+ call->gtCallAddr = compiler->gtNewLclvNode(m_targetLclNum, TYP_I_IMPL);
+ call->gtCallCookie = nullptr;
+ call->gtCallMoreFlags &= ~GTF_CALL_M_DELEGATE_INV;
+
+ GenTree* thisOffset =
+ compiler->gtNewIconNode((ssize_t)compiler->eeGetEEInfo()->offsetOfDelegateInstance, TYP_I_IMPL);
+ CallArg* thisArg = call->gtArgs.GetThisArg();
+ GenTree* delegateObj = thisArg->GetNode();
+
+ assert(delegateObj->OperIsLocal());
+ GenTree* newThis =
+ compiler->gtNewOperNode(GT_ADD, TYP_BYREF, compiler->gtCloneExpr(delegateObj), thisOffset);
+ newThis = compiler->gtNewIndir(TYP_REF, newThis);
+
+ thisArg->SetEarlyNode(newThis);
+ }
+ }
+
compiler->fgInsertStmtAtEnd(elseBlock, newStmt);
// Set the original statement to a nop.
@@ -1005,6 +1179,62 @@ class IndirectCallTransformer
private:
unsigned returnTemp;
Statement* lastStmt;
+
+ //------------------------------------------------------------------------
+ // CreateTreeForLookup: Create a tree representing a lookup of a method address.
+ //
+ // Arguments:
+ // methHnd - the handle for the method the lookup is for
+ // lookup - lookup information for the address
+ //
+ // Returns:
+ // A node representing the lookup.
+ //
+ GenTree* CreateTreeForLookup(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup)
+ {
+ switch (lookup.accessType)
+ {
+ case IAT_VALUE:
+ {
+ return CreateFunctionTargetAddr(methHnd, lookup);
+ }
+ case IAT_PVALUE:
+ {
+ GenTree* tree = CreateFunctionTargetAddr(methHnd, lookup);
+ tree = compiler->gtNewIndir(TYP_I_IMPL, tree);
+ tree->gtFlags |= GTF_IND_NONFAULTING | GTF_IND_INVARIANT;
+ tree->gtFlags &= ~GTF_EXCEPT;
+ return tree;
+ }
+ case IAT_PPVALUE:
+ {
+ noway_assert(!"Unexpected IAT_PPVALUE");
+ return nullptr;
+ }
+ case IAT_RELPVALUE:
+ {
+ GenTree* addr = CreateFunctionTargetAddr(methHnd, lookup);
+ GenTree* tree = CreateFunctionTargetAddr(methHnd, lookup);
+ tree = compiler->gtNewIndir(TYP_I_IMPL, tree);
+ tree->gtFlags |= GTF_IND_NONFAULTING | GTF_IND_INVARIANT;
+ tree->gtFlags &= ~GTF_EXCEPT;
+ tree = compiler->gtNewOperNode(GT_ADD, TYP_I_IMPL, tree, addr);
+ return tree;
+ }
+ default:
+ {
+ noway_assert(!"Bad accessType");
+ return nullptr;
+ }
+ }
+ }
+
+ GenTree* CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup)
+ {
+ GenTree* con = compiler->gtNewIconHandleNode((size_t)lookup.addr, GTF_ICON_FTN_ADDR);
+ INDEBUG(con->AsIntCon()->gtTargetHandle = (size_t)methHnd);
+ return con;
+ }
};
// Runtime lookup with dynamic dictionary expansion transformer,
diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp
index 546eb2aa376f9d..7f3c65fde97ff4 100644
--- a/src/coreclr/jit/inline.cpp
+++ b/src/coreclr/jit/inline.cpp
@@ -643,7 +643,8 @@ void InlineContext::DumpXml(FILE* file, unsigned indent)
// stmt - statement containing the call (if known)
// description - string describing the context of the decision
-InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description)
+InlineResult::InlineResult(
+ Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description, bool doNotReport)
: m_RootCompiler(nullptr)
, m_Policy(nullptr)
, m_Call(call)
@@ -652,7 +653,9 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stm
, m_Callee(nullptr)
, m_ImportedILSize(0)
, m_Description(description)
- , m_Reported(false)
+ , m_successResult(INLINE_PASS)
+ , m_DoNotReport(doNotReport)
+ , m_reportFailureAsVmFailure(false)
{
// Set the compiler instance
m_RootCompiler = compiler->impInlineRoot();
@@ -683,6 +686,12 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stm
{
m_Callee = m_Call->AsCall()->gtCallMethHnd;
}
+
+ if (!m_DoNotReport)
+ {
+ COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
+ comp->beginInlining(m_Caller, m_Callee);
+ }
}
//------------------------------------------------------------------------
@@ -701,7 +710,7 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stm
// We use the inlCallee member to track the method since logically
// it is the callee here.
-InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description)
+InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description, bool doNotReport)
: m_RootCompiler(nullptr)
, m_Policy(nullptr)
, m_Call(nullptr)
@@ -709,7 +718,9 @@ InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, con
, m_Caller(nullptr)
, m_Callee(method)
, m_Description(description)
- , m_Reported(false)
+ , m_successResult(INLINE_PASS)
+ , m_DoNotReport(doNotReport)
+ , m_reportFailureAsVmFailure(false)
{
// Set the compiler instance
m_RootCompiler = compiler->impInlineRoot();
@@ -717,6 +728,12 @@ InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, con
// Set the policy
const bool isPrejitRoot = true;
m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
+
+ if (!m_DoNotReport)
+ {
+ COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
+ comp->beginInlining(m_Caller, m_Callee);
+ }
}
//------------------------------------------------------------------------
@@ -731,8 +748,6 @@ InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, con
// EE. Optionally update the method attribute to NOINLINE if
// observation and policy warrant.
//
-// All this can be suppressed if desired by calling setReported()
-// before the InlineResult goes out of scope.
void InlineResult::Report()
{
@@ -753,13 +768,13 @@ void InlineResult::Report()
#endif // DEBUG
// If we weren't actually inlining, user may have suppressed
- // reporting via setReported(). If so, do nothing.
- if (m_Reported)
+ // reporting. If so, do nothing.
+ if (m_DoNotReport)
{
return;
}
- m_Reported = true;
+ m_DoNotReport = true;
#ifdef DEBUG
const char* callee = nullptr;
@@ -798,7 +813,7 @@ void InlineResult::Report()
}
}
- if (IsDecided())
+ if (IsDecided() || m_reportFailureAsVmFailure || m_successResult != INLINE_PASS)
{
const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
JITLOG_THIS(m_RootCompiler, (LL_INFO100000, format, m_Description, ResultString(), ReasonString()));
diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h
index e5ec2ea3cc10e5..f21a77da4fa147 100644
--- a/src/coreclr/jit/inline.h
+++ b/src/coreclr/jit/inline.h
@@ -344,11 +344,12 @@ class InlineResult
public:
// Construct a new InlineResult to help evaluate a
// particular call for inlining.
- InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description);
+ InlineResult(
+ Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description, bool doNotReport = false);
// Construct a new InlineResult to evaluate a particular
// method to see if it is inlineable.
- InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description);
+ InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description, bool doNotReport = false);
// Has the policy determined this inline should fail?
bool IsFailure() const
@@ -483,18 +484,42 @@ class InlineResult
// Result that can be reported back to the runtime
CorInfoInline Result() const
{
+ if (m_reportFailureAsVmFailure)
+ return INLINE_CHECK_CAN_INLINE_VMFAIL;
+
+ if (m_successResult != INLINE_PASS)
+ return m_successResult;
+
return InlGetCorInfoInlineDecision(m_Policy->GetDecision());
}
// String describing the decision made
const char* ResultString() const
{
+ if (m_reportFailureAsVmFailure)
+ return "VM Reported !CanInline";
+
+ if (m_successResult == INLINE_PREJIT_SUCCESS)
+ return "PreJIT Success";
+
+ if (m_successResult == INLINE_CHECK_CAN_INLINE_SUCCESS)
+ return "CheckCanInline Success";
+
return InlGetDecisionString(m_Policy->GetDecision());
}
// String describing the reason for the decision
const char* ReasonString() const
{
+ if (m_reportFailureAsVmFailure)
+ return "VM Reported !CanInline";
+
+ if (m_successResult == INLINE_PREJIT_SUCCESS)
+ return "PreJIT Success";
+
+ if (m_successResult == INLINE_CHECK_CAN_INLINE_SUCCESS)
+ return "CheckCanInline Success";
+
return InlGetObservationString(m_Policy->GetObservation());
}
@@ -504,12 +529,15 @@ class InlineResult
return m_Policy;
}
- // SetReported indicates that this particular result doesn't need
- // to be reported back to the runtime, either because the runtime
- // already knows, or we aren't actually inlining yet.
- void SetReported()
+ // Set the code that shall be reported if the InlineResult is a success
+ void SetSuccessResult(CorInfoInline inlineSuccessCode)
+ {
+ m_successResult = inlineSuccessCode;
+ }
+
+ void SetVMFailure()
{
- m_Reported = true;
+ m_reportFailureAsVmFailure = true;
}
// Get the InlineContext for this inline.
@@ -544,13 +572,15 @@ class InlineResult
CORINFO_METHOD_HANDLE m_Callee;
unsigned m_ImportedILSize; // estimated size of imported IL
const char* m_Description;
- bool m_Reported;
+ CorInfoInline m_successResult;
+ bool m_DoNotReport;
+ bool m_reportFailureAsVmFailure;
};
-// ClassProfileCandidateInfo provides information about
+// HandleHistogramProfileCandidateInfo provides information about
// profiling an indirect or virtual call.
//
-struct ClassProfileCandidateInfo
+struct HandleHistogramProfileCandidateInfo
{
IL_OFFSET ilOffset;
unsigned probeIndex;
@@ -559,7 +589,7 @@ struct ClassProfileCandidateInfo
// GuardedDevirtualizationCandidateInfo provides information about
// a potential target of a virtual or interface call.
//
-struct GuardedDevirtualizationCandidateInfo : ClassProfileCandidateInfo
+struct GuardedDevirtualizationCandidateInfo : HandleHistogramProfileCandidateInfo
{
CORINFO_CLASS_HANDLE guardedClassHandle;
CORINFO_METHOD_HANDLE guardedMethodHandle;
diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h
index 0a8527a6393fe5..e766df67304d8b 100644
--- a/src/coreclr/jit/instrsxarch.h
+++ b/src/coreclr/jit/instrsxarch.h
@@ -594,8 +594,8 @@ INST3(LAST_AVXVNNI_INSTRUCTION, "LAST_AVXVNNI_INSTRUCTION", IUM_WR, BAD_CODE, BA
// BMI1
INST3(FIRST_BMI_INSTRUCTION, "FIRST_BMI_INSTRUCTION", IUM_WR, BAD_CODE, BAD_CODE, BAD_CODE, INS_FLAGS_None)
INST3(andn, "andn", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF2), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Logical AND NOT
-INST3(blsi, "blsi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Extract Lowest Set Isolated Bit
-INST3(blsmsk, "blsmsk", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_Flags_IsDstDstSrcAVXInstruction) // Get Mask Up to Lowest Set Bit
+INST3(blsi, "blsi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Extract Lowest Set Isolated Bit
+INST3(blsmsk, "blsmsk", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Get Mask Up to Lowest Set Bit
INST3(blsr, "blsr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Reset Lowest Set Bit
INST3(bextr, "bextr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Bit Field Extract
diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h
index d4ea7ab0dcb910..110c079ee58c40 100644
--- a/src/coreclr/jit/jit.h
+++ b/src/coreclr/jit/jit.h
@@ -327,8 +327,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
typedef class ICorJitInfo* COMP_HANDLE;
-const CORINFO_CLASS_HANDLE NO_CLASS_HANDLE = nullptr;
-const CORINFO_FIELD_HANDLE NO_FIELD_HANDLE = nullptr;
+const CORINFO_CLASS_HANDLE NO_CLASS_HANDLE = nullptr;
+const CORINFO_FIELD_HANDLE NO_FIELD_HANDLE = nullptr;
+const CORINFO_METHOD_HANDLE NO_METHOD_HANDLE = nullptr;
/*****************************************************************************/
@@ -336,7 +337,8 @@ typedef unsigned IL_OFFSET;
const IL_OFFSET BAD_IL_OFFSET = 0xffffffff;
-const unsigned BAD_VAR_NUM = UINT_MAX;
+const unsigned BAD_VAR_NUM = UINT_MAX;
+const uint16_t BAD_LCL_OFFSET = UINT16_MAX;
// Code can't be more than 2^31 in any direction. This is signed, so it should be used for anything that is
// relative to something else.
@@ -838,19 +840,26 @@ T dspOffset(T o)
#endif // !defined(DEBUG)
-struct LikelyClassRecord
+struct LikelyClassMethodRecord
{
- CORINFO_CLASS_HANDLE clsHandle;
- UINT32 likelihood;
+ intptr_t handle;
+ UINT32 likelihood;
};
-extern "C" UINT32 WINAPI getLikelyClasses(LikelyClassRecord* pLikelyClasses,
+extern "C" UINT32 WINAPI getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses,
UINT32 maxLikelyClasses,
ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
int32_t ilOffset);
+extern "C" UINT32 WINAPI getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods,
+ UINT32 maxLikelyMethods,
+ ICorJitInfo::PgoInstrumentationSchema* schema,
+ UINT32 countSchemaItems,
+ BYTE* pInstrumentationData,
+ int32_t ilOffset);
+
/*****************************************************************************/
#endif //_JIT_H_
/*****************************************************************************/
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 0d701bb958ce30..9ae04f0505953b 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -354,9 +354,9 @@ CONFIG_INTEGER(JitDisableSimdVN, W("JitDisableSimdVN"), 0) // Default 0, ValueNu
//
CONFIG_INTEGER(JitConstCSE, W("JitConstCSE"), 0)
-#define CONST_CSE_ENABLE_ARM64 0
+#define CONST_CSE_ENABLE_ARM 0
#define CONST_CSE_DISABLE_ALL 1
-#define CONST_CSE_ENABLE_ARM64_NO_SHARING 2
+#define CONST_CSE_ENABLE_ARM_NO_SHARING 2
#define CONST_CSE_ENABLE_ALL 3
#define CONST_CSE_ENABLE_ALL_NO_SHARING 4
@@ -548,10 +548,12 @@ CONFIG_INTEGER(JitMinimalJitProfiling, W("JitMinimalJitProfiling"), 1)
CONFIG_INTEGER(JitMinimalPrejitProfiling, W("JitMinimalPrejitProfiling"), 0)
CONFIG_INTEGER(JitProfileCasts, W("JitProfileCasts"), 0) // Profile castclass/isinst
-CONFIG_INTEGER(JitConsumeProfileForCasts, W("JitConsumeProfileForCasts"), 0) // Consume profile data (if any) for
+CONFIG_INTEGER(JitConsumeProfileForCasts, W("JitConsumeProfileForCasts"), 1) // Consume profile data (if any) for
// castclass/isinst
CONFIG_INTEGER(JitClassProfiling, W("JitClassProfiling"), 1) // Profile virtual and interface calls
+CONFIG_INTEGER(JitDelegateProfiling, W("JitDelegateProfiling"), 1) // Profile resolved delegate call targets
+CONFIG_INTEGER(JitVTableProfiling, W("JitVTableProfiling"), 0) // Profile resolved vtable call targets
CONFIG_INTEGER(JitEdgeProfiling, W("JitEdgeProfiling"), 1) // Profile edges instead of blocks
CONFIG_INTEGER(JitCollect64BitCounts, W("JitCollect64BitCounts"), 0) // Collect counts as 64-bit values.
@@ -603,6 +605,8 @@ CONFIG_STRING(JitFunctionFile, W("JitFunctionFile"))
// 1: disable frames that save FP/LR registers with the callee-saved registers (at the top of the frame)
// 2: force all frames to use the frame types that save FP/LR registers with the callee-saved registers (at the top
// of the frame)
+// 3: force all frames to use the frame types that save FP/LR registers with the callee-saved registers (at the top
+// of the frame) and also force using the large funclet frame variation (frame 5) if possible.
CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSavedRegisters"), 0)
#endif // defined(TARGET_ARM64)
diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp
index 25074f8827472e..4ca754e9c993b9 100644
--- a/src/coreclr/jit/lclmorph.cpp
+++ b/src/coreclr/jit/lclmorph.cpp
@@ -836,10 +836,10 @@ class LocalAddressVisitor final : public GenTreeVisitor
LclVarDsc* varDsc = m_compiler->lvaGetDesc(val.LclNum());
- if (varDsc->lvPromoted || varDsc->lvIsStructField || m_compiler->lvaIsImplicitByRefLocal(val.LclNum()))
+ if (varDsc->lvPromoted || varDsc->lvIsStructField)
{
- // TODO-ADDR: For now we ignore promoted and "implicit by ref" variables,
- // they require additional changes in subsequent phases.
+ // TODO-ADDR: For now we ignore promoted variables, they require
+ // additional changes in subsequent phases.
return;
}
@@ -1017,11 +1017,10 @@ class LocalAddressVisitor final : public GenTreeVisitor
return IndirTransform::None;
}
- if (varDsc->lvPromoted || varDsc->lvIsStructField || m_compiler->lvaIsImplicitByRefLocal(val.LclNum()))
+ if (varDsc->lvPromoted || varDsc->lvIsStructField)
{
- // TODO-ADDR: For now we ignore promoted and "implicit by ref" variables,
- // they require additional changes in subsequent phases
- // (e.g. fgMorphImplicitByRefArgs does not handle LCL_FLD nodes).
+ // TODO-ADDR: For now we ignore promoted variables, they require additional
+ // changes in subsequent phases.
return IndirTransform::None;
}
@@ -1037,7 +1036,6 @@ class LocalAddressVisitor final : public GenTreeVisitor
{
// TODO-ADDR: Skip SIMD indirs for now, SIMD typed LCL_FLDs works most of the time
// but there are exceptions - fgMorphFieldAssignToSimdSetElement for example.
- // And more importantly, SIMD call args have to be wrapped in OBJ nodes currently.
return IndirTransform::None;
}
@@ -1050,10 +1048,9 @@ class LocalAddressVisitor final : public GenTreeVisitor
return IndirTransform::None;
}
- if ((user == nullptr) || !user->OperIs(GT_ASG, GT_RETURN))
+ if ((user == nullptr) || !user->OperIs(GT_ASG, GT_CALL, GT_RETURN))
{
- // TODO-ADDR: call args require extra work because currently they must
- // be wrapped in OBJ nodes so we can't replace those with local nodes.
+ // TODO-ADDR: remove unused indirections.
return IndirTransform::None;
}
@@ -1076,7 +1073,6 @@ class LocalAddressVisitor final : public GenTreeVisitor
//
enum class StructMatch
{
- Exact,
Compatible,
Partial
};
@@ -1085,49 +1081,49 @@ class LocalAddressVisitor final : public GenTreeVisitor
assert(varDsc->GetLayout() != nullptr);
StructMatch match = StructMatch::Partial;
- if (val.Offset() == 0)
+ if ((val.Offset() == 0) && ClassLayout::AreCompatible(indirLayout, varDsc->GetLayout()))
{
- if (indirLayout->GetClassHandle() == varDsc->GetStructHnd())
- {
- match = StructMatch::Exact;
- }
- else if (ClassLayout::AreCompatible(indirLayout, varDsc->GetLayout()))
- {
- match = StructMatch::Compatible;
- }
+ match = StructMatch::Compatible;
}
// Current matrix of matches/users/types:
//
- // |------------|------|---------|---------|
- // | STRUCT | CALL | ASG | RETURN |
- // |------------|------|---------|---------|
- // | Exact | None | LCL_VAR | LCL_VAR |
- // | Compatible | None | LCL_VAR | LCL_VAR |
- // | Partial | None | OBJ | LCL_FLD |
- // |------------|------|---------|---------|
+ // |------------|---------|-------------|---------|
+ // | STRUCT | CALL(*) | ASG | RETURN |
+ // |------------|---------|-------------|---------|
+ // | Compatible | LCL_VAR | LCL_VAR | LCL_VAR |
+ // | Partial | LCL_FLD | OBJ/LCL_FLD | LCL_FLD |
+ // |------------|---------|-------------|---------|
+ //
+ // * - On x86/Windows x64 only.
//
- // |------------|------|---------|---------|----------|
- // | SIMD | CALL | ASG | RETURN | HWI/SIMD |
- // |------------|------|---------|---------|----------|
- // | Exact | None | None | None | None |
- // | Compatible | None | None | None | None |
- // | Partial | None | None | None | None |
- // |------------|------|---------|---------|----------|
+ // |------------|------|------|--------|----------|
+ // | SIMD | CALL | ASG | RETURN | HWI/SIMD |
+ // |------------|------|------|--------|----------|
+ // | Compatible | None | None | None | None |
+ // | Partial | None | None | None | None |
+ // |------------|------|------|--------|----------|
//
// TODO-ADDR: delete all the "None" entries and always
// transform local nodes into LCL_VAR or LCL_FLD.
- assert(indir->TypeIs(TYP_STRUCT) && user->OperIs(GT_ASG, GT_RETURN));
+ assert(indir->TypeIs(TYP_STRUCT) && user->OperIs(GT_ASG, GT_CALL, GT_RETURN));
*pStructLayout = indirLayout;
- if ((match == StructMatch::Exact) || (match == StructMatch::Compatible))
+ if (user->IsCall())
+ {
+#if !defined(WINDOWS_AMD64_ABI) && !defined(TARGET_X86)
+ return IndirTransform::None;
+#endif // !defined(WINDOWS_AMD64_ABI) && !defined(TARGET_X86)
+ }
+
+ if (match == StructMatch::Compatible)
{
return IndirTransform::LclVar;
}
- if (user->OperIs(GT_ASG))
+ if (user->OperIs(GT_ASG) && (indir == user->AsOp()->gtGetOp1()))
{
return IndirTransform::ObjAddrLclFld;
}
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index cc12c8a3837dad..a1439c7e4bd5c3 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -1494,14 +1494,14 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
varDsc->lvOverlappingFields = StructHasOverlappingFields(cFlags);
}
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
- varDsc->lvIsImplicitByRef = 0;
-#elif defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
varDsc->lvIsImplicitByRef = 0;
- varDsc->lvIs4Field1 = 0;
- varDsc->lvIs4Field2 = 0;
- varDsc->lvIsSplit = 0;
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#endif // FEATURE_IMPLICIT_BYREFS
+#ifdef TARGET_LOONGARCH64
+ varDsc->lvIs4Field1 = 0;
+ varDsc->lvIs4Field2 = 0;
+ varDsc->lvIsSplit = 0;
+#endif // TARGET_LOONGARCH64
// Set the lvType (before this point it is TYP_UNDEF).
@@ -1832,7 +1832,7 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE
const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * FP_REGSIZE_BYTES;
#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64)
#else // !FEATURE_SIMD
- const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * sizeof(double);
+ const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * sizeof(double);
#endif // !FEATURE_SIMD
assert((BYTE)MaxOffset == MaxOffset); // because lvaStructFieldInfo.fldOffset is byte-sized
@@ -2535,12 +2535,10 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum)
compiler->compLongUsed = true;
}
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
-
+#if FEATURE_IMPLICIT_BYREFS
// Reset the implicitByRef flag.
fieldVarDsc->lvIsImplicitByRef = 0;
-
-#endif
+#endif // FEATURE_IMPLICIT_BYREFS
// Do we have a parameter that can be enregistered?
//
@@ -2890,6 +2888,61 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister
#endif
}
+//------------------------------------------------------------------------
+// lvaIsImplicitByRefLocal: Is the local an "implicit byref" parameter?
+//
+// We term structs passed via pointers to shadow copies "implicit byrefs".
+// They are used on Windows x64 for structs 3, 5, 6, 7, > 8 bytes in size,
+// and on ARM64/LoongArch64 for structs larger than 16 bytes.
+//
+// They are "byrefs" because the VM sometimes uses memory allocated on the
+// GC heap for the shadow copies.
+//
+// Arguments:
+// lclNum - The local in question
+//
+// Return Value:
+// Whether "lclNum" refers to an implicit byref.
+//
+bool Compiler::lvaIsImplicitByRefLocal(unsigned lclNum) const
+{
+#if FEATURE_IMPLICIT_BYREFS
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
+ if (varDsc->lvIsImplicitByRef)
+ {
+ assert(varDsc->lvIsParam);
+
+ assert(varTypeIsStruct(varDsc) || (varDsc->TypeGet() == TYP_BYREF));
+ return true;
+ }
+#endif // FEATURE_IMPLICIT_BYREFS
+ return false;
+}
+
+//------------------------------------------------------------------------
+// lvaIsLocalImplicitlyAccessedByRef: Will this local be accessed indirectly?
+//
+// Arguments:
+// lclNum - The number of local in question
+//
+// Return Value:
+// If "lclNum" is an implicit byref parameter, or its dependently promoted
+// field, "true", otherwise, "false".
+//
+// Notes:
+// This method is only meaningful before the locals have been morphed into
+// explicit indirections.
+//
+bool Compiler::lvaIsLocalImplicitlyAccessedByRef(unsigned lclNum) const
+{
+ if (lvaGetDesc(lclNum)->lvIsStructField)
+ {
+ return lvaIsImplicitByRefLocal(lvaGetDesc(lclNum)->lvParentLcl);
+ }
+
+ return lvaIsImplicitByRefLocal(lclNum);
+}
+
// Returns true if this local var is a multireg struct.
// TODO-Throughput: This does a lookup on the class handle, and in the outgoing arg context
// this information is already available on the CallArgABIInformation, and shouldn't need to be
@@ -2951,7 +3004,7 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool
CorInfoType simdBaseJitType = CORINFO_TYPE_UNDEF;
varDsc->lvType = impNormStructType(typeHnd, &simdBaseJitType);
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
// Mark implicit byref struct parameters
if (varDsc->lvIsParam && !varDsc->lvIsStructField)
{
@@ -2964,7 +3017,7 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool
varDsc->lvIsImplicitByRef = 1;
}
}
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#endif // FEATURE_IMPLICIT_BYREFS
#if FEATURE_SIMD
if (simdBaseJitType != CORINFO_TYPE_UNDEF)
@@ -4181,49 +4234,57 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
/* Is this an assignment to a local variable? */
- if (op1->gtOper == GT_LCL_VAR && op2->gtType != TYP_BOOL)
+ if (op1->gtOper == GT_LCL_VAR)
{
- /* Only simple assignments allowed for booleans */
+ LclVarDsc* varDsc = lvaGetDesc(op1->AsLclVarCommon());
- if (tree->gtOper != GT_ASG)
+ if (varDsc->lvPinned && varDsc->lvAllDefsAreNoGc)
{
- goto NOT_BOOL;
+ if (!op2->IsNotGcDef())
+ {
+ varDsc->lvAllDefsAreNoGc = false;
+ }
}
- /* Is the RHS clearly a boolean value? */
-
- switch (op2->gtOper)
+ if (op2->gtType != TYP_BOOL)
{
- unsigned lclNum;
+ /* Only simple assignments allowed for booleans */
- case GT_CNS_INT:
+ if (tree->gtOper != GT_ASG)
+ {
+ goto NOT_BOOL;
+ }
- if (op2->AsIntCon()->gtIconVal == 0)
- {
- break;
- }
- if (op2->AsIntCon()->gtIconVal == 1)
- {
- break;
- }
+ /* Is the RHS clearly a boolean value? */
- // Not 0 or 1, fall through ....
- FALLTHROUGH;
+ switch (op2->gtOper)
+ {
+ case GT_CNS_INT:
- default:
+ if (op2->AsIntCon()->gtIconVal == 0)
+ {
+ break;
+ }
+ if (op2->AsIntCon()->gtIconVal == 1)
+ {
+ break;
+ }
- if (op2->OperIsCompare())
- {
- break;
- }
+ // Not 0 or 1, fall through ....
+ FALLTHROUGH;
- NOT_BOOL:
+ default:
- lclNum = op1->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
+ if (op2->OperIsCompare())
+ {
+ break;
+ }
- lvaTable[lclNum].lvIsBoolean = false;
- break;
+ NOT_BOOL:
+
+ varDsc->lvIsBoolean = false;
+ break;
+ }
}
}
}
@@ -4278,7 +4339,8 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
{
if (lvaVarAddrExposed(lclNum))
{
- varDsc->lvIsBoolean = false;
+ varDsc->lvIsBoolean = false;
+ varDsc->lvAllDefsAreNoGc = false;
}
if (tree->gtOper == GT_LCL_FLD)
@@ -4750,6 +4812,8 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
{
varDsc->lvSingleDef = varDsc->lvIsParam;
varDsc->lvSingleDefRegCandidate = varDsc->lvIsParam;
+
+ varDsc->lvAllDefsAreNoGc = (varDsc->lvImplicitlyReferenced == false);
}
}
@@ -4868,6 +4932,13 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
varDsc->lvImplicitlyReferenced = 1;
}
}
+
+ if (varDsc->lvPinned && varDsc->lvAllDefsAreNoGc)
+ {
+ varDsc->lvPinned = 0;
+
+ JITDUMP("V%02u was unpinned as all def candidates were local.\n", lclNum);
+ }
}
}
@@ -6332,7 +6403,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
{
codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(false); // Disable using new frames
}
- else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 2)
+ else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3))
{
codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(true); // Force using new frames
}
diff --git a/src/coreclr/jit/likelyclass.cpp b/src/coreclr/jit/likelyclass.cpp
index 632c9ce8b847b9..6943c0c5a83a9e 100644
--- a/src/coreclr/jit/likelyclass.cpp
+++ b/src/coreclr/jit/likelyclass.cpp
@@ -26,45 +26,45 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Data item in class profile histogram
//
-struct LikelyClassHistogramEntry
+struct LikelyClassMethodHistogramEntry
{
- // Class that was observed at runtime
- INT_PTR m_mt; // This may be an "unknown type handle"
+ // Handle that was observed at runtime
+ INT_PTR m_handle; // This may be an "unknown handle"
// Number of observations in the table
unsigned m_count;
};
// Summarizes a ClassProfile table by forming a Histogram
//
-struct LikelyClassHistogram
+struct LikelyClassMethodHistogram
{
- LikelyClassHistogram(INT_PTR* histogramEntries, unsigned entryCount);
+ LikelyClassMethodHistogram(INT_PTR* histogramEntries, unsigned entryCount);
// Sum of counts from all entries in the histogram. This includes "unknown" entries which are not captured in
// m_histogram
unsigned m_totalCount;
- // Rough guess at count of unknown types
- unsigned m_unknownTypes;
+ // Rough guess at count of unknown handles
+ unsigned m_unknownHandles;
// Histogram entries, in no particular order.
- LikelyClassHistogramEntry m_histogram[HISTOGRAM_MAX_SIZE_COUNT];
- UINT32 countHistogramElements = 0;
+ LikelyClassMethodHistogramEntry m_histogram[HISTOGRAM_MAX_SIZE_COUNT];
+ UINT32 countHistogramElements = 0;
- LikelyClassHistogramEntry HistogramEntryAt(unsigned index)
+ LikelyClassMethodHistogramEntry HistogramEntryAt(unsigned index)
{
return m_histogram[index];
}
};
//------------------------------------------------------------------------
-// LikelyClassHistogram::LikelyClassHistgram: construct a new histogram
+// LikelyClassMethodHistogram::LikelyClassMethodHistgram: construct a new histogram
//
// Arguments:
// histogramEntries - pointer to the table portion of a ClassProfile* object (see corjit.h)
// entryCount - number of entries in the table to examine
//
-LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned entryCount)
+LikelyClassMethodHistogram::LikelyClassMethodHistogram(INT_PTR* histogramEntries, unsigned entryCount)
{
- m_unknownTypes = 0;
+ m_unknownHandles = 0;
m_totalCount = 0;
uint32_t unknownTypeHandleMask = 0;
@@ -83,7 +83,7 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
unsigned h = 0;
for (; h < countHistogramElements; h++)
{
- if (m_histogram[h].m_mt == currentEntry)
+ if (m_histogram[h].m_handle == currentEntry)
{
m_histogram[h].m_count++;
found = true;
@@ -97,8 +97,8 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
{
continue;
}
- LikelyClassHistogramEntry newEntry;
- newEntry.m_mt = currentEntry;
+ LikelyClassMethodHistogramEntry newEntry;
+ newEntry.m_handle = currentEntry;
newEntry.m_count = 1;
m_histogram[countHistogramElements++] = newEntry;
}
@@ -106,42 +106,28 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
}
//------------------------------------------------------------------------
-// getLikelyClasses: find class profile data for an IL offset, and return the most likely classes
-//
-// Arguments:
-// pLikelyClasses - [OUT] array of likely classes sorted by likelihood (descending). It must be
-// at least of 'maxLikelyClasses' (next argument) length.
-// The array consists of pairs "clsHandle - likelihood" ordered by likelihood
-// (descending) where likelihood can be any value in [0..100] range. clsHandle
-// is never null for [0..) range, Items in
-// [..maxLikelyClasses) are zeroed if the number
-// of classes seen is less than maxLikelyClasses provided.
-// maxLikelyClasses - limit for likely classes to output
-// schema - profile schema
-// countSchemaItems - number of items in the schema
-// pInstrumentationData - associated data
-// ilOffset - il offset of the callvirt
+// getLikelyClassesOrMethods:
+// Find class/method profile data for an IL offset, and return the most
+// likely classes/methods.
//
-// Returns:
-// Estimated number of classes seen at runtime
-//
-// Notes:
-// A "monomorphic" call site will return likelihood 100 and number of entries = 1.
-//
-// This is used by the devirtualization logic below, and by crossgen2 when producing
-// the R2R image (to reduce the sizecost of carrying the type histogram)
-//
-// This code can runs without a jit instance present, so JITDUMP and related
-// cannot be used.
+// This is a common entrypoint for getLikelyClasses and getLikelyMethods.
+// See documentation for those for more information.
//
-extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord* pLikelyClasses,
- UINT32 maxLikelyClasses,
- ICorJitInfo::PgoInstrumentationSchema* schema,
- UINT32 countSchemaItems,
- BYTE* pInstrumentationData,
- int32_t ilOffset)
+static unsigned getLikelyClassesOrMethods(LikelyClassMethodRecord* pLikelyEntries,
+ UINT32 maxLikelyClasses,
+ ICorJitInfo::PgoInstrumentationSchema* schema,
+ UINT32 countSchemaItems,
+ BYTE* pInstrumentationData,
+ int32_t ilOffset,
+ bool types)
{
- ZeroMemory(pLikelyClasses, maxLikelyClasses * sizeof(*pLikelyClasses));
+ ICorJitInfo::PgoInstrumentationKind histogramKind =
+ types ? ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes
+ : ICorJitInfo::PgoInstrumentationKind::HandleHistogramMethods;
+ ICorJitInfo::PgoInstrumentationKind compressedKind = types ? ICorJitInfo::PgoInstrumentationKind::GetLikelyClass
+ : ICorJitInfo::PgoInstrumentationKind::GetLikelyMethod;
+
+ memset(pLikelyEntries, 0, maxLikelyClasses * sizeof(*pLikelyEntries));
if (schema == nullptr)
{
@@ -153,17 +139,16 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
if (schema[i].ILOffset != ilOffset)
continue;
- if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass) &&
- (schema[i].Count == 1))
+ if ((schema[i].InstrumentationKind == compressedKind) && (schema[i].Count == 1))
{
- INT_PTR result = *(INT_PTR*)(pInstrumentationData + schema[i].Offset);
+ intptr_t result = *(intptr_t*)(pInstrumentationData + schema[i].Offset);
if (ICorJitInfo::IsUnknownHandle(result))
{
return 0;
}
- assert(result != 0); // we don't expect zero in GetLikelyClass
- pLikelyClasses[0].likelihood = (UINT32)(schema[i].Other & 0xFF);
- pLikelyClasses[0].clsHandle = (CORINFO_CLASS_HANDLE)result;
+ assert(result != 0); // we don't expect zero in GetLikelyClass/GetLikelyMethod
+ pLikelyEntries[0].likelihood = (UINT32)(schema[i].Other & 0xFF);
+ pLikelyEntries[0].handle = result;
return 1;
}
@@ -172,11 +157,11 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
(schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount);
if (isHistogramCount && (schema[i].Count == 1) && ((i + 1) < countSchemaItems) &&
- (schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes))
+ (schema[i + 1].InstrumentationKind == histogramKind))
{
// Form a histogram
//
- LikelyClassHistogram h((INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);
+ LikelyClassMethodHistogram h((INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);
// Use histogram count as number of classes estimate
// Report back what we've learned
@@ -189,45 +174,45 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
case 1:
{
- LikelyClassHistogramEntry const hist0 = h.HistogramEntryAt(0);
+ LikelyClassMethodHistogramEntry const hist0 = h.HistogramEntryAt(0);
// Fast path for monomorphic cases
- if (ICorJitInfo::IsUnknownHandle(hist0.m_mt))
+ if (ICorJitInfo::IsUnknownHandle(hist0.m_handle))
{
return 0;
}
- pLikelyClasses[0].likelihood = 100;
- pLikelyClasses[0].clsHandle = (CORINFO_CLASS_HANDLE)hist0.m_mt;
+ pLikelyEntries[0].likelihood = 100;
+ pLikelyEntries[0].handle = hist0.m_handle;
return 1;
}
case 2:
{
- LikelyClassHistogramEntry const hist0 = h.HistogramEntryAt(0);
- LikelyClassHistogramEntry const hist1 = h.HistogramEntryAt(1);
// Fast path for two classes
- if ((hist0.m_count >= hist1.m_count) && !ICorJitInfo::IsUnknownHandle(hist0.m_mt))
+ LikelyClassMethodHistogramEntry const hist0 = h.HistogramEntryAt(0);
+ LikelyClassMethodHistogramEntry const hist1 = h.HistogramEntryAt(1);
+ if ((hist0.m_count >= hist1.m_count) && !ICorJitInfo::IsUnknownHandle(hist0.m_handle))
{
- pLikelyClasses[0].likelihood = (100 * hist0.m_count) / h.m_totalCount;
- pLikelyClasses[0].clsHandle = (CORINFO_CLASS_HANDLE)hist0.m_mt;
+ pLikelyEntries[0].likelihood = (100 * hist0.m_count) / h.m_totalCount;
+ pLikelyEntries[0].handle = hist0.m_handle;
- if ((maxLikelyClasses > 1) && !ICorJitInfo::IsUnknownHandle(hist1.m_mt))
+ if ((maxLikelyClasses > 1) && !ICorJitInfo::IsUnknownHandle(hist1.m_handle))
{
- pLikelyClasses[1].likelihood = (100 * hist1.m_count) / h.m_totalCount;
- pLikelyClasses[1].clsHandle = (CORINFO_CLASS_HANDLE)hist1.m_mt;
+ pLikelyEntries[1].likelihood = (100 * hist1.m_count) / h.m_totalCount;
+ pLikelyEntries[1].handle = hist1.m_handle;
return 2;
}
return 1;
}
- if (!ICorJitInfo::IsUnknownHandle(hist1.m_mt))
+ if (!ICorJitInfo::IsUnknownHandle(hist1.m_handle))
{
- pLikelyClasses[0].likelihood = (100 * hist1.m_count) / h.m_totalCount;
- pLikelyClasses[0].clsHandle = (CORINFO_CLASS_HANDLE)hist1.m_mt;
+ pLikelyEntries[0].likelihood = (100 * hist1.m_count) / h.m_totalCount;
+ pLikelyEntries[0].handle = hist1.m_handle;
- if ((maxLikelyClasses > 1) && !ICorJitInfo::IsUnknownHandle(hist0.m_mt))
+ if ((maxLikelyClasses > 1) && !ICorJitInfo::IsUnknownHandle(hist0.m_handle))
{
- pLikelyClasses[1].likelihood = (100 * hist0.m_count) / h.m_totalCount;
- pLikelyClasses[1].clsHandle = (CORINFO_CLASS_HANDLE)hist0.m_mt;
+ pLikelyEntries[1].likelihood = (100 * hist0.m_count) / h.m_totalCount;
+ pLikelyEntries[1].handle = hist0.m_handle;
return 2;
}
return 1;
@@ -237,14 +222,14 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
default:
{
- LikelyClassHistogramEntry sortedEntries[HISTOGRAM_MAX_SIZE_COUNT];
+ LikelyClassMethodHistogramEntry sortedEntries[HISTOGRAM_MAX_SIZE_COUNT];
// Since this method can be invoked without a jit instance we can't use any existing allocators
unsigned knownHandles = 0;
for (unsigned m = 0; m < h.countHistogramElements; m++)
{
- LikelyClassHistogramEntry const hist = h.HistogramEntryAt(m);
- if (!ICorJitInfo::IsUnknownHandle(hist.m_mt))
+ LikelyClassMethodHistogramEntry const hist = h.HistogramEntryAt(m);
+ if (!ICorJitInfo::IsUnknownHandle(hist.m_handle))
{
sortedEntries[knownHandles++] = hist;
}
@@ -252,7 +237,8 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
// sort by m_count (descending)
jitstd::sort(sortedEntries, sortedEntries + knownHandles,
- [](const LikelyClassHistogramEntry& h1, const LikelyClassHistogramEntry& h2) -> bool {
+ [](const LikelyClassMethodHistogramEntry& h1,
+ const LikelyClassMethodHistogramEntry& h2) -> bool {
return h1.m_count > h2.m_count;
});
@@ -260,9 +246,9 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
for (size_t hIdx = 0; hIdx < numberOfClasses; hIdx++)
{
- LikelyClassHistogramEntry const hc = sortedEntries[hIdx];
- pLikelyClasses[hIdx].clsHandle = (CORINFO_CLASS_HANDLE)hc.m_mt;
- pLikelyClasses[hIdx].likelihood = hc.m_count * 100 / h.m_totalCount;
+ LikelyClassMethodHistogramEntry const hc = sortedEntries[hIdx];
+ pLikelyEntries[hIdx].handle = hc.m_handle;
+ pLikelyEntries[hIdx].likelihood = hc.m_count * 100 / h.m_totalCount;
}
return numberOfClasses;
}
@@ -276,80 +262,57 @@ extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassRecord*
}
//------------------------------------------------------------------------
-// getRandomClass: find class profile data for an IL offset, and return
-// one of the possible classes at random
+// getLikelyClasses: find class profile data for an IL offset, and return the most likely classes
//
// Arguments:
+// pLikelyClasses - [OUT] array of likely classes sorted by likelihood (descending). It must be
+// at least of 'maxLikelyClasses' (next argument) length.
+// The array consists of pairs "clsHandle - likelihood" ordered by likelihood
+// (descending) where likelihood can be any value in [0..100] range. clsHandle
+// is never null for [0..) range, Items in
+// [..maxLikelyClasses) are zeroed if the number
+// of classes seen is less than maxLikelyClasses provided.
+// maxLikelyClasses - limit for likely classes to output
// schema - profile schema
// countSchemaItems - number of items in the schema
// pInstrumentationData - associated data
// ilOffset - il offset of the callvirt
-// random - randomness generator
//
// Returns:
-// Randomly observed class, or nullptr.
+// Estimated number of classes seen at runtime
+//
+// Notes:
+// A "monomorphic" call site will return likelihood 100 and number of entries = 1.
+//
+// This is used by the devirtualization logic below, and by crossgen2 when producing
+// the R2R image (to reduce the sizecost of carrying the type histogram)
+//
+// This code can runs without a jit instance present, so JITDUMP and related
+// cannot be used.
//
-CORINFO_CLASS_HANDLE Compiler::getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
- UINT32 countSchemaItems,
- BYTE* pInstrumentationData,
- int32_t ilOffset,
- CLRRandom* random)
+extern "C" DLLEXPORT UINT32 WINAPI getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses,
+ UINT32 maxLikelyClasses,
+ ICorJitInfo::PgoInstrumentationSchema* schema,
+ UINT32 countSchemaItems,
+ BYTE* pInstrumentationData,
+ int32_t ilOffset)
{
- if (schema == nullptr)
- {
- return NO_CLASS_HANDLE;
- }
-
- for (COUNT_T i = 0; i < countSchemaItems; i++)
- {
- if (schema[i].ILOffset != (int32_t)ilOffset)
- {
- continue;
- }
-
- if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass) &&
- (schema[i].Count == 1))
- {
- INT_PTR result = *(INT_PTR*)(pInstrumentationData + schema[i].Offset);
- if (ICorJitInfo::IsUnknownHandle(result))
- {
- return NO_CLASS_HANDLE;
- }
- else
- {
- return (CORINFO_CLASS_HANDLE)result;
- }
- }
-
- bool isHistogramCount =
- (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramIntCount) ||
- (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramLongCount);
-
- if (isHistogramCount && (schema[i].Count == 1) && ((i + 1) < countSchemaItems) &&
- (schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::HandleHistogramTypes))
- {
- // Form a histogram
- //
- LikelyClassHistogram h((INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);
-
- if (h.countHistogramElements == 0)
- {
- return NO_CLASS_HANDLE;
- }
-
- // Choose an entry at random.
- //
- unsigned randomEntryIndex = random->Next(0, h.countHistogramElements);
- LikelyClassHistogramEntry randomEntry = h.HistogramEntryAt(randomEntryIndex);
-
- if (ICorJitInfo::IsUnknownHandle(randomEntry.m_mt))
- {
- return NO_CLASS_HANDLE;
- }
-
- return (CORINFO_CLASS_HANDLE)randomEntry.m_mt;
- }
- }
+ return getLikelyClassesOrMethods(pLikelyClasses, maxLikelyClasses, schema, countSchemaItems, pInstrumentationData,
+ ilOffset, true);
+}
- return NO_CLASS_HANDLE;
+//------------------------------------------------------------------------
+// getLikelyMethods: find method profile data for an IL offset, and return the most likely methods
+//
+// See documentation on getLikelyClasses above.
+//
+extern "C" DLLEXPORT UINT32 WINAPI getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods,
+ UINT32 maxLikelyMethods,
+ ICorJitInfo::PgoInstrumentationSchema* schema,
+ UINT32 countSchemaItems,
+ BYTE* pInstrumentationData,
+ int32_t ilOffset)
+{
+ return getLikelyClassesOrMethods(pLikelyMethods, maxLikelyMethods, schema, countSchemaItems, pInstrumentationData,
+ ilOffset, false);
}
diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp
index 915f19525ffc89..05c96837bc4ef0 100644
--- a/src/coreclr/jit/loopcloning.cpp
+++ b/src/coreclr/jit/loopcloning.cpp
@@ -1724,6 +1724,25 @@ bool Compiler::optIsLoopClonable(unsigned loopInd)
return false;
}
+ // Reject cloning if this is a mid-entry loop and the entry has non-loop predecessors other than its head.
+ // This loop may be part of a larger looping construct that we didn't recognize.
+ //
+ // We should really fix this in optCanonicalizeLoop.
+ //
+ if (!loop.lpIsTopEntry())
+ {
+ for (BasicBlock* const entryPred : loop.lpEntry->PredBlocks())
+ {
+ if ((entryPred != loop.lpHead) && !loop.lpContains(entryPred))
+ {
+ JITDUMP("Loop cloning: rejecting loop " FMT_LP
+ ". Is not top entry, and entry has multiple non-loop preds.\n",
+ loopInd);
+ return false;
+ }
+ }
+ }
+
// We've previously made a decision whether to have separate return epilogs, or branch to one.
// There's a GCInfo limitation in the x86 case, so that there can be no more than SET_EPILOGCNT_MAX separate
// epilogs. Other architectures have a limit of 4 here for "historical reasons", but this should be revisited
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 98bd11f0fd2d38..0450beb2b26073 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -388,16 +388,6 @@ GenTree* Lowering::LowerNode(GenTree* node)
break;
#endif
-#if !defined(TARGET_ARMARCH) && !defined(TARGET_LOONGARCH64)
- // TODO-ARMARCH-CQ: We should contain this as long as the offset fits.
- case GT_OBJ:
- if (node->AsObj()->Addr()->OperIsLocalAddr())
- {
- node->AsObj()->Addr()->SetContained();
- }
- break;
-#endif // !TARGET_ARMARCH
-
case GT_KEEPALIVE:
node->gtGetOp1()->SetRegOptional();
break;
@@ -1023,7 +1013,6 @@ bool Lowering::TryLowerSwitchToBitTest(
GenTree* bitTest = comp->gtNewOperNode(GT_BT, TYP_VOID, bitTableIcon, switchValue);
bitTest->gtFlags |= GTF_SET_FLAGS;
GenTreeCC* jcc = new (comp, GT_JCC) GenTreeCC(GT_JCC, bbSwitchCondition);
- jcc->gtFlags |= GTF_USE_FLAGS;
LIR::AsRange(bbSwitch).InsertAfter(switchValue, bitTableIcon, bitTest, jcc);
@@ -1031,9 +1020,6 @@ bool Lowering::TryLowerSwitchToBitTest(
#endif // TARGET_XARCH
}
-// NOTE: this method deliberately does not update the call arg table. It must only
-// be used by NewPutArg and LowerArg; these functions are responsible for updating
-// the call arg table as necessary.
void Lowering::ReplaceArgWithPutArgOrBitcast(GenTree** argSlot, GenTree* putArgOrBitcast)
{
assert(argSlot != nullptr);
@@ -1069,12 +1055,7 @@ void Lowering::ReplaceArgWithPutArgOrBitcast(GenTree** argSlot, GenTree* putArgO
// Notes:
// For System V systems with native struct passing (i.e. UNIX_AMD64_ABI defined)
// this method allocates a single GT_PUTARG_REG for 1 eightbyte structs and a GT_FIELD_LIST of two GT_PUTARG_REGs
-// for two eightbyte structs.
-//
-// For STK passed structs the method generates GT_PUTARG_STK tree. For System V systems with native struct passing
-// (i.e. UNIX_AMD64_ABI defined) this method also sets the GC pointers count and the pointers
-// layout object, so the codegen of the GT_PUTARG_STK could use this for optimizing copying to the stack by value.
-// (using block copy primitives for non GC pointers and a single TARGET_POINTER_SIZE copy with recording GC info.)
+// for two eightbyte structs. For STK passed structs the method generates GT_PUTARG_STK tree.
//
GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg, var_types type)
{
@@ -1086,19 +1067,6 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
bool isOnStack = (callArg->AbiInfo.GetRegNum() == REG_STK);
-#if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64)
- // Mark contained when we pass struct
- // GT_FIELD_LIST is always marked contained when it is generated
- if (type == TYP_STRUCT)
- {
- arg->SetContained();
- if ((arg->OperGet() == GT_OBJ) && (arg->AsObj()->Addr()->OperGet() == GT_LCL_VAR_ADDR))
- {
- MakeSrcContained(arg, arg->AsObj()->Addr());
- }
- }
-#endif
-
#if FEATURE_ARG_SPLIT
// Struct can be split into register(s) and stack on ARM
if (compFeatureArgSplit() && callArg->AbiInfo.IsSplit())
@@ -1120,10 +1088,7 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
callArg->AbiInfo.GetStackByteSize(),
#endif
callArg->AbiInfo.NumRegs, call, putInIncomingArgArea);
- // If struct argument is morphed to GT_FIELD_LIST node(s),
- // we can know GC info by type of each GT_FIELD_LIST node.
- // So we skip setting GC Pointer info.
- //
+
GenTreePutArgSplit* argSplit = putArg->AsPutArgSplit();
for (unsigned regIndex = 0; regIndex < callArg->AbiInfo.NumRegs; regIndex++)
{
@@ -1132,6 +1097,12 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
if (arg->OperGet() == GT_OBJ)
{
+ arg->SetContained();
+ if (arg->AsObj()->Addr()->OperGet() == GT_LCL_VAR_ADDR)
+ {
+ MakeSrcContained(arg, arg->AsObj()->Addr());
+ }
+
ClassLayout* layout = arg->AsObj()->GetLayout();
// Set type of registers
@@ -1206,8 +1177,6 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
#ifdef DEBUG
// Make sure state is correct. The PUTARG_STK has TYP_VOID, as it doesn't produce
// a result. So the type of its operand must be the correct type to push on the stack.
- // For a FIELD_LIST, this will be the type of the field (not the type of the arg),
- // but otherwise it is generally the type of the operand.
callArg->CheckIsStruct();
#endif
@@ -1245,64 +1214,15 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
#endif
call, putInIncomingArgArea);
-#ifdef FEATURE_PUT_STRUCT_ARG_STK
- // If the ArgTabEntry indicates that this arg is a struct
- // get and store the number of slots that are references.
- // This is later used in the codegen for PUT_ARG_STK implementation
- // for struct to decide whether and how many single eight-byte copies
- // to be done (only for reference slots), so gcinfo is emitted.
- // For non-reference slots faster/smaller size instructions are used -
- // pair copying using XMM registers or rep mov instructions.
+#if defined(DEBUG) && defined(FEATURE_PUT_STRUCT_ARG_STK)
if (callArg->AbiInfo.IsStruct)
{
- // We use GT_OBJ only for non-lclVar, non-SIMD, non-FIELD_LIST struct arguments.
- if (arg->OperIsLocal())
- {
- // This must have a type with a known size (SIMD or has been morphed to a primitive type).
- assert(arg->TypeGet() != TYP_STRUCT);
- }
- else if (arg->OperIs(GT_OBJ))
+ // We use GT_OBJ only for non-SIMD struct arguments.
+ if (arg->OperIs(GT_OBJ))
{
assert(!varTypeIsSIMD(arg));
-
-#ifdef TARGET_X86
- // On x86 VM lies about the type of a struct containing a pointer sized
- // integer field by returning the type of its field as the type of struct.
- // Such struct can be passed in a register depending its position in
- // parameter list. VM does this unwrapping only one level and therefore
- // a type like Struct Foo { Struct Bar { int f}} awlays needs to be
- // passed on stack. Also, VM doesn't lie about type of such a struct
- // when it is a field of another struct. That is VM doesn't lie about
- // the type of Foo.Bar
- //
- // We now support the promotion of fields that are of type struct.
- // However we only support a limited case where the struct field has a
- // single field and that single field must be a scalar type. Say Foo.Bar
- // field is getting passed as a parameter to a call, Since it is a TYP_STRUCT,
- // as per x86 ABI it should always be passed on stack. Therefore GenTree
- // node under a PUTARG_STK could be GT_OBJ(GT_LCL_VAR_ADDR(v1)), where
- // local v1 could be a promoted field standing for Foo.Bar. Note that
- // the type of v1 will be the type of field of Foo.Bar.f when Foo is
- // promoted. That is v1 will be a scalar type. In this case we need to
- // pass v1 on stack instead of in a register.
- //
- // TODO-PERF: replace GT_OBJ(GT_LCL_VAR_ADDR(v1)) with v1 if v1 is
- // a scalar type and the width of GT_OBJ matches the type size of v1.
- // Note that this cannot be done till call node arguments are morphed
- // because we should not lose the fact that the type of argument is
- // a struct so that the arg gets correctly marked to be passed on stack.
- GenTree* objOp1 = arg->gtGetOp1();
- if (objOp1->OperGet() == GT_LCL_VAR_ADDR)
- {
- unsigned lclNum = objOp1->AsLclVarCommon()->GetLclNum();
- if (comp->lvaTable[lclNum].lvType != TYP_STRUCT)
- {
- comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
- }
- }
-#endif // TARGET_X86
}
- else if (!arg->OperIs(GT_FIELD_LIST))
+ else if (!arg->TypeIs(TYP_STRUCT))
{
#ifdef TARGET_ARM
assert((callArg->AbiInfo.GetStackSlotsNumber() == 1) ||
@@ -1312,7 +1232,7 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg,
#endif
}
}
-#endif // FEATURE_PUT_STRUCT_ARG_STK
+#endif // defined(DEBUG) && defined(FEATURE_PUT_STRUCT_ARG_STK)
}
}
@@ -1459,6 +1379,13 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg, bool late)
ReplaceArgWithPutArgOrBitcast(ppArg, putArg);
}
}
+
+ arg = *ppArg;
+
+ if (arg->OperIs(GT_PUTARG_STK))
+ {
+ LowerPutArgStk(arg->AsPutArgStk());
+ }
}
#if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64)
@@ -2283,10 +2210,58 @@ void Lowering::LowerCFGCall(GenTreeCall* call)
}
GenTree* callTarget = call->gtCallType == CT_INDIRECT ? call->gtCallAddr : call->gtControlExpr;
- if ((callTarget == nullptr) || callTarget->IsIntegralConst())
+ if (callTarget == nullptr)
{
- // This is a direct call, no CFG check is necessary.
- return;
+ assert((call->gtCallType != CT_INDIRECT) && (!call->IsVirtual() || call->IsVirtualStubRelativeIndir()));
+ if (!call->IsVirtual())
+ {
+ // Direct call with stashed address
+ return;
+ }
+
+ // This is a VSD call with the call target being null because we are
+ // supposed to load it from the indir cell. Due to CFG we will need
+ // this address twice, and at least on ARM64 we do not want to
+ // materialize the constant both times.
+ CallArg* indirCellArg = call->gtArgs.FindWellKnownArg(WellKnownArg::VirtualStubCell);
+ assert((indirCellArg != nullptr) && indirCellArg->GetNode()->OperIs(GT_PUTARG_REG));
+
+ GenTreeOp* putArgNode = indirCellArg->GetNode()->AsOp();
+ LIR::Use indirCellArgUse(BlockRange(), &putArgNode->gtOp1, putArgNode);
+
+ // On non-xarch, we create a local even for constants. On xarch cloning
+ // the constant is better since it can be contained in the load below.
+ bool cloneConsts = false;
+#ifdef TARGET_XARCH
+ cloneConsts = true;
+#endif
+
+ GenTree* indirCellClone;
+
+ if (indirCellArgUse.Def()->OperIs(GT_LCL_VAR) || (cloneConsts && indirCellArgUse.Def()->IsCnsIntOrI()))
+ {
+ indirCellClone = comp->gtClone(indirCellArgUse.Def());
+ }
+ else
+ {
+ unsigned newLcl = indirCellArgUse.ReplaceWithLclVar(comp);
+ indirCellClone = comp->gtNewLclvNode(newLcl, TYP_I_IMPL);
+ }
+
+ callTarget = Ind(indirCellClone);
+ LIR::Range controlExprRange = LIR::SeqTree(comp, callTarget);
+ ContainCheckRange(controlExprRange);
+
+ BlockRange().InsertBefore(call, std::move(controlExprRange));
+ call->gtControlExpr = callTarget;
+ }
+ else
+ {
+ if (callTarget->IsIntegralConst())
+ {
+ // This is a direct call, no CFG check is necessary.
+ return;
+ }
}
CFGCallKind cfgKind = call->GetCFGCallKind();
@@ -2727,7 +2702,6 @@ GenTree* Lowering::DecomposeLongCompare(GenTree* cmp)
GenTree* jcc = cmpUse.User();
jcc->AsOp()->gtOp1 = nullptr;
jcc->ChangeOper(GT_JCC);
- jcc->gtFlags |= GTF_USE_FLAGS;
jcc->AsCC()->gtCondition = GenCondition::FromIntegralRelop(condition, cmp->IsUnsigned());
}
else
@@ -2735,7 +2709,6 @@ GenTree* Lowering::DecomposeLongCompare(GenTree* cmp)
cmp->AsOp()->gtOp1 = nullptr;
cmp->AsOp()->gtOp2 = nullptr;
cmp->ChangeOper(GT_SETCC);
- cmp->gtFlags |= GTF_USE_FLAGS;
cmp->AsCC()->gtCondition = GenCondition::FromIntegralRelop(condition, cmp->IsUnsigned());
}
@@ -2983,8 +2956,6 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp)
cmpUse.ReplaceWith(cc);
}
- cc->gtFlags |= GTF_USE_FLAGS;
-
return cmp->gtNext;
}
#endif // TARGET_XARCH
@@ -3052,7 +3023,6 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp)
GenCondition condition = GenCondition::FromIntegralRelop(cmp);
cc->ChangeOper(ccOp);
cc->AsCC()->gtCondition = condition;
- cc->gtFlags |= GTF_USE_FLAGS;
return next;
}
@@ -3270,7 +3240,6 @@ GenTreeCC* Lowering::LowerNodeCC(GenTree* node, GenCondition condition)
if (cc != nullptr)
{
node->gtFlags |= GTF_SET_FLAGS;
- cc->gtFlags |= GTF_USE_FLAGS;
}
// Remove the chain of EQ/NE(x, 0) relop nodes, if any. Note that if a SETCC was
@@ -3539,13 +3508,13 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore)
// Do it now.
GenTreeIndir* indir = src->AsIndir();
LowerIndir(indir);
+ }
#if defined(TARGET_XARCH)
- if (varTypeIsSmall(lclRegType))
- {
- indir->SetDontExtend();
- }
-#endif // TARGET_XARCH
+ if (varTypeIsSmall(lclRegType))
+ {
+ src->SetDontExtend();
}
+#endif // TARGET_XARCH
}
convertToStoreObj = false;
#else // TARGET_ARM64
@@ -3814,18 +3783,18 @@ void Lowering::LowerCallStruct(GenTreeCall* call)
if (GlobalJitOptions::compFeatureHfa)
{
- if (comp->IsHfa(call))
+ if (comp->IsHfa(call->gtRetClsHnd))
{
#if defined(TARGET_ARM64)
- assert(comp->GetHfaCount(call) == 1);
+ assert(comp->GetHfaCount(call->gtRetClsHnd) == 1);
#elif defined(TARGET_ARM)
// ARM returns double in 2 float registers, but
// `call->HasMultiRegRetVal()` count double registers.
- assert(comp->GetHfaCount(call) <= 2);
+ assert(comp->GetHfaCount(call->gtRetClsHnd) <= 2);
#else // !TARGET_ARM64 && !TARGET_ARM
NYI("Unknown architecture");
#endif // !TARGET_ARM64 && !TARGET_ARM
- var_types hfaType = comp->GetHfaType(call);
+ var_types hfaType = comp->GetHfaType(call->gtRetClsHnd);
if (call->TypeIs(hfaType))
{
return;
@@ -5127,7 +5096,7 @@ GenTree* Lowering::LowerVirtualStubCall(GenTreeCall* call)
// Skip inserting the indirection node to load the address that is already
// computed in the VSD stub arg register as a hidden parameter. Instead during the
// codegen, just load the call target from there.
- shouldOptimizeVirtualStubCall = !comp->opts.IsCFGEnabled();
+ shouldOptimizeVirtualStubCall = true;
#endif
if (!shouldOptimizeVirtualStubCall)
@@ -5395,7 +5364,7 @@ bool Lowering::TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* par
// Check if we can "contain" LEA(BFIZ) in order to extend 32bit index to 64bit as part of load/store.
if ((index != nullptr) && index->OperIs(GT_BFIZ) && index->gtGetOp1()->OperIs(GT_CAST) &&
- index->gtGetOp2()->IsCnsIntOrI() && (varTypeIsIntegral(targetType) || varTypeIsFloating(targetType)))
+ index->gtGetOp2()->IsCnsIntOrI() && !varTypeIsStruct(targetType))
{
// BFIZ node is a binary op where op1 is GT_CAST and op2 is GT_CNS_INT
GenTreeCast* cast = index->gtGetOp1()->AsCast();
@@ -5838,7 +5807,8 @@ GenTree* Lowering::LowerConstIntDivOrMod(GenTree* node)
#if defined(TARGET_ARM64)
if (divMod->OperIs(GT_MOD) && divisor->IsIntegralConstPow2())
{
- return LowerModPow2(node);
+ LowerModPow2(node);
+ return node->gtNext;
}
assert(node->OperGet() != GT_MOD);
#endif // TARGET_ARM64
@@ -7374,9 +7344,9 @@ bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode)
}
#if defined(TARGET_XARCH)
- if (varTypeIsSmall(regType) && src->OperIs(GT_IND))
+ if (varTypeIsSmall(regType) && src->OperIs(GT_IND, GT_LCL_FLD))
{
- src->AsIndir()->SetDontExtend();
+ src->SetDontExtend();
}
#endif // TARGET_XARCH
@@ -7414,6 +7384,7 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
if (arg->IsCnsFltOrDbl())
{
+ noway_assert(constArgCount < ArrLen(constArgValues));
constArgValues[constArgCount] = static_cast(arg->AsDblCon()->gtDconVal);
constArgCount++;
}
@@ -7426,10 +7397,14 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
BlockRange().Remove(arg);
}
- assert(sizeof(constArgValues) == 16);
+ // For SIMD12, even though there might be 12 bytes of constants, we need to store 16 bytes of data
+ // since we've bashed the node the TYP_SIMD16 and do a 16-byte indirection.
+ assert(varTypeIsSIMD(simdNode));
+ const unsigned cnsSize = genTypeSize(simdNode);
+ assert(cnsSize <= sizeof(constArgValues));
- unsigned cnsSize = sizeof(constArgValues);
- unsigned cnsAlign = (comp->compCodeOpt() != Compiler::SMALL_CODE) ? cnsSize : 1;
+ const unsigned cnsAlign =
+ (comp->compCodeOpt() != Compiler::SMALL_CODE) ? cnsSize : emitter::dataSection::MIN_DATA_ALIGN;
CORINFO_FIELD_HANDLE hnd =
comp->GetEmitter()->emitBlkConst(constArgValues, cnsSize, cnsAlign, simdNode->GetSimdBaseType());
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index 7e6acf6f03009b..08eeb581433367 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -351,11 +351,12 @@ class Lowering final : public Phase
GenTree* TryLowerAndOpToResetLowestSetBit(GenTreeOp* andNode);
GenTree* TryLowerAndOpToExtractLowestSetBit(GenTreeOp* andNode);
GenTree* TryLowerAndOpToAndNot(GenTreeOp* andNode);
+ GenTree* TryLowerXorOpToGetMaskUpToLowestSetBit(GenTreeOp* xorNode);
void LowerBswapOp(GenTreeOp* node);
#elif defined(TARGET_ARM64)
bool IsValidConstForMovImm(GenTreeHWIntrinsic* node);
void LowerHWIntrinsicFusedMultiplyAddScalar(GenTreeHWIntrinsic* node);
- GenTree* LowerModPow2(GenTree* node);
+ void LowerModPow2(GenTree* node);
GenTree* LowerAddForPossibleContainment(GenTreeOp* node);
#endif // !TARGET_XARCH && !TARGET_ARM64
#endif // FEATURE_HW_INTRINSICS
diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp
index 5ee0c27767ee1c..ed77d2a954f175 100644
--- a/src/coreclr/jit/lowerarmarch.cpp
+++ b/src/coreclr/jit/lowerarmarch.cpp
@@ -104,6 +104,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const
case GT_LE:
case GT_GE:
case GT_GT:
+ case GT_CMP:
case GT_BOUNDS_CHECK:
return emitter::emitIns_valid_imm_for_cmp(immVal, size);
case GT_AND:
@@ -574,6 +575,44 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
addr->SetContained();
}
+//------------------------------------------------------------------------
+// LowerPutArgStk: Lower a GT_PUTARG_STK.
+//
+// Arguments:
+// putArgStk - The node to lower
+//
+void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
+{
+ GenTree* src = putArgStk->Data();
+
+ if (src->TypeIs(TYP_STRUCT))
+ {
+ // STRUCT args (FIELD_LIST / OBJ / LCL_VAR / LCL_FLD) will always be contained.
+ MakeSrcContained(putArgStk, src);
+
+ // TODO-ADDR: always perform this transformation in local morph and delete this code.
+ if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr())
+ {
+ GenTreeLclVarCommon* lclAddrNode = src->AsObj()->Addr()->AsLclVarCommon();
+ unsigned lclNum = lclAddrNode->GetLclNum();
+ unsigned lclOffs = lclAddrNode->GetLclOffs();
+ ClassLayout* layout = src->AsObj()->GetLayout();
+
+ src->ChangeOper(GT_LCL_FLD);
+ src->AsLclFld()->SetLclNum(lclNum);
+ src->AsLclFld()->SetLclOffs(lclOffs);
+ src->AsLclFld()->SetLayout(layout);
+
+ BlockRange().Remove(lclAddrNode);
+ }
+ else if (src->OperIs(GT_LCL_VAR))
+ {
+ // TODO-1stClassStructs: support struct enregistration here by retyping "src" to its register type.
+ comp->lvaSetVarDoNotEnregister(src->AsLclVar()->GetLclNum() DEBUGARG(DoNotEnregisterReason::IsStructArg));
+ }
+ }
+}
+
//------------------------------------------------------------------------
// LowerCast: Lower GT_CAST(srcType, DstType) nodes.
//
@@ -661,41 +700,26 @@ void Lowering::LowerRotate(GenTree* tree)
// Arguments:
// tree - the node to lower
//
-// Return Value:
-// A new tree node if it changed.
-//
// Notes:
-// {expr} % {cns}
-// Logically turns into:
-// let a = {expr}
-// if a > 0 then (a & ({cns} - 1)) else -(-a & ({cns} - 1))
-// which then turns into:
-// and reg1, reg0, #({cns} - 1)
-// negs reg0, reg0
-// and reg0, reg0, #({cns} - 1)
-// csneg reg0, reg1, reg0, mi
// TODO: We could do this optimization in morph but we do not have
// a conditional select op in HIR. At some point, we may
// introduce such an op.
-GenTree* Lowering::LowerModPow2(GenTree* node)
+void Lowering::LowerModPow2(GenTree* node)
{
assert(node->OperIs(GT_MOD));
- GenTree* mod = node;
- GenTree* dividend = mod->gtGetOp1();
- GenTree* divisor = mod->gtGetOp2();
+ GenTreeOp* mod = node->AsOp();
+ GenTree* dividend = mod->gtGetOp1();
+ GenTree* divisor = mod->gtGetOp2();
+
+ JITDUMP("Lower: optimize X MOD POW2");
assert(divisor->IsIntegralConstPow2());
const var_types type = mod->TypeGet();
assert((type == TYP_INT) || (type == TYP_LONG));
- LIR::Use use;
- if (!BlockRange().TryGetUse(node, &use))
- {
- return nullptr;
- }
-
- ssize_t cnsValue = static_cast(divisor->AsIntConCommon()->IntegralValue()) - 1;
+ ssize_t divisorCnsValue = static_cast(divisor->AsIntConCommon()->IntegralValue());
+ ssize_t divisorCnsValueMinusOne = divisorCnsValue - 1;
BlockRange().Remove(divisor);
@@ -707,39 +731,64 @@ GenTree* Lowering::LowerModPow2(GenTree* node)
GenTree* dividend2 = comp->gtClone(dividend);
BlockRange().InsertAfter(dividend, dividend2);
- GenTreeIntCon* cns = comp->gtNewIconNode(cnsValue, type);
+ GenTreeIntCon* cns = comp->gtNewIconNode(divisorCnsValueMinusOne, type);
BlockRange().InsertAfter(dividend2, cns);
GenTree* const trueExpr = comp->gtNewOperNode(GT_AND, type, dividend, cns);
BlockRange().InsertAfter(cns, trueExpr);
LowerNode(trueExpr);
- GenTree* const neg = comp->gtNewOperNode(GT_NEG, type, dividend2);
- neg->gtFlags |= GTF_SET_FLAGS;
- BlockRange().InsertAfter(trueExpr, neg);
-
- GenTreeIntCon* cns2 = comp->gtNewIconNode(cnsValue, type);
- BlockRange().InsertAfter(neg, cns2);
-
- GenTree* const falseExpr = comp->gtNewOperNode(GT_AND, type, neg, cns2);
- BlockRange().InsertAfter(cns2, falseExpr);
- LowerNode(falseExpr);
-
- GenTree* const cc = comp->gtNewOperNode(GT_CSNEG_MI, type, trueExpr, falseExpr);
- cc->gtFlags |= GTF_USE_FLAGS;
-
- JITDUMP("Lower: optimize X MOD POW2");
- DISPNODE(mod);
- JITDUMP("to:\n");
- DISPNODE(cc);
-
- BlockRange().InsertBefore(mod, cc);
- ContainCheckNode(cc);
- BlockRange().Remove(mod);
+ if (divisorCnsValue == 2)
+ {
+ // {expr} % 2
+ // Logically turns into:
+ // let a = {expr}
+ // if a < 0 then -(a & 1) else (a & 1)
+ // which then turns into:
+ // and reg1, reg0, #1
+ // cmp reg0, #0
+ // cneg reg0, reg1, lt
+
+ GenTreeIntCon* cnsZero = comp->gtNewIconNode(0, type);
+ BlockRange().InsertAfter(trueExpr, cnsZero);
+
+ GenTree* const cmp = comp->gtNewOperNode(GT_CMP, type, dividend2, cnsZero);
+ cmp->gtFlags |= GTF_SET_FLAGS;
+ BlockRange().InsertAfter(cnsZero, cmp);
+ LowerNode(cmp);
- use.ReplaceWith(cc);
+ mod->ChangeOper(GT_CNEG_LT);
+ mod->gtOp1 = trueExpr;
+ }
+ else
+ {
+ // {expr} % {cns}
+ // Logically turns into:
+ // let a = {expr}
+ // if a > 0 then (a & ({cns} - 1)) else -(-a & ({cns} - 1))
+ // which then turns into:
+ // and reg1, reg0, #({cns} - 1)
+ // negs reg0, reg0
+ // and reg0, reg0, #({cns} - 1)
+ // csneg reg0, reg1, reg0, mi
+
+ GenTree* const neg = comp->gtNewOperNode(GT_NEG, type, dividend2);
+ neg->gtFlags |= GTF_SET_FLAGS;
+ BlockRange().InsertAfter(trueExpr, neg);
+
+ GenTreeIntCon* cns2 = comp->gtNewIconNode(divisorCnsValueMinusOne, type);
+ BlockRange().InsertAfter(neg, cns2);
+
+ GenTree* const falseExpr = comp->gtNewOperNode(GT_AND, type, neg, cns2);
+ BlockRange().InsertAfter(cns2, falseExpr);
+ LowerNode(falseExpr);
+
+ mod->ChangeOper(GT_CSNEG_MI);
+ mod->gtOp1 = trueExpr;
+ mod->gtOp2 = falseExpr;
+ }
- return cc->gtNext;
+ ContainCheckNode(mod);
}
//------------------------------------------------------------------------
diff --git a/src/coreclr/jit/lowerloongarch64.cpp b/src/coreclr/jit/lowerloongarch64.cpp
index 7a2cde0c4f5fbe..1de03b57e8e92a 100644
--- a/src/coreclr/jit/lowerloongarch64.cpp
+++ b/src/coreclr/jit/lowerloongarch64.cpp
@@ -416,6 +416,30 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
addr->SetContained();
}
+//------------------------------------------------------------------------
+// LowerPutArgStk: Lower a GT_PUTARG_STK.
+//
+// Arguments:
+// putArgStk - The node to lower
+//
+void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
+{
+ GenTree* src = putArgStk->Data();
+
+ if (src->TypeIs(TYP_STRUCT))
+ {
+ // STRUCT args (FIELD_LIST / OBJ) will always be contained.
+ MakeSrcContained(putArgStk, src);
+
+ // Additionally, codegen supports containment of local addresses under OBJs.
+ if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIs(GT_LCL_VAR_ADDR))
+ {
+ // TODO-LOONGARCH64-CQ: support containment of LCL_FLD_ADDR too.
+ MakeSrcContained(src, src->AsObj()->Addr());
+ }
+ }
+}
+
//------------------------------------------------------------------------
// LowerCast: Lower GT_CAST(srcType, DstType) nodes.
//
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 46990d9f6dbc64..e8be8b615c6d85 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -174,24 +174,35 @@ GenTree* Lowering::LowerMul(GenTreeOp* mul)
GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp)
{
#ifdef FEATURE_HW_INTRINSICS
- if (comp->opts.OptimizationEnabled() && binOp->OperIs(GT_AND) && varTypeIsIntegral(binOp))
+ if (comp->opts.OptimizationEnabled() && varTypeIsIntegral(binOp))
{
- GenTree* replacementNode = TryLowerAndOpToAndNot(binOp);
- if (replacementNode != nullptr)
+ if (binOp->OperIs(GT_AND))
{
- return replacementNode->gtNext;
- }
+ GenTree* replacementNode = TryLowerAndOpToAndNot(binOp);
+ if (replacementNode != nullptr)
+ {
+ return replacementNode->gtNext;
+ }
- replacementNode = TryLowerAndOpToResetLowestSetBit(binOp);
- if (replacementNode != nullptr)
- {
- return replacementNode->gtNext;
- }
+ replacementNode = TryLowerAndOpToResetLowestSetBit(binOp);
+ if (replacementNode != nullptr)
+ {
+ return replacementNode->gtNext;
+ }
- replacementNode = TryLowerAndOpToExtractLowestSetBit(binOp);
- if (replacementNode != nullptr)
+ replacementNode = TryLowerAndOpToExtractLowestSetBit(binOp);
+ if (replacementNode != nullptr)
+ {
+ return replacementNode->gtNext;
+ }
+ }
+ else if (binOp->OperIs(GT_XOR))
{
- return replacementNode->gtNext;
+ GenTree* replacementNode = TryLowerXorOpToGetMaskUpToLowestSetBit(binOp);
+ if (replacementNode != nullptr)
+ {
+ return replacementNode->gtNext;
+ }
}
}
#endif
@@ -455,14 +466,11 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
// LowerPutArgStk: Lower a GT_PUTARG_STK.
//
// Arguments:
-// tree - The node of interest
-//
-// Return Value:
-// None.
+// putArgStk - The node of interest
//
void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
{
- GenTree* src = putArgStk->gtGetOp1();
+ GenTree* src = putArgStk->Data();
bool srcIsLocal = src->OperIsLocalRead();
if (src->OperIs(GT_FIELD_LIST))
@@ -533,9 +541,11 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
#ifdef FEATURE_PUT_STRUCT_ARG_STK
if (src->TypeIs(TYP_STRUCT))
{
- ClassLayout* layout = src->AsObj()->GetLayout();
+ assert(src->OperIs(GT_OBJ) || src->OperIsLocalRead());
+
+ ClassLayout* layout = src->GetLayout(comp);
var_types regType = layout->GetRegisterType();
- srcIsLocal |= src->AsObj()->Addr()->OperIsLocalAddr();
+ srcIsLocal |= src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr();
if (regType == TYP_UNDEF)
{
@@ -545,30 +555,24 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
// The cpyXXXX code is rather complex and this could cause it to be more complex, but
// it might be the right thing to do.
- unsigned size = putArgStk->GetStackByteSize();
- unsigned loadSize = layout->GetSize();
-
- assert(loadSize <= size);
+ // If possible, widen the load, this results in more compact code.
+ unsigned loadSize = srcIsLocal ? roundUp(layout->GetSize(), TARGET_POINTER_SIZE) : layout->GetSize();
+ putArgStk->SetArgLoadSize(loadSize);
// TODO-X86-CQ: The helper call either is not supported on x86 or required more work
// (I don't know which).
-
if (!layout->HasGCPtr())
{
#ifdef TARGET_X86
// Codegen for "Kind::Push" will always load bytes in TARGET_POINTER_SIZE
- // chunks. As such, the correctness of this code depends on the fact that
- // morph will copy any "mis-sized" (too small) non-local OBJs into a temp,
- // thus preventing any possible out-of-bounds memory reads.
- assert(((layout->GetSize() % TARGET_POINTER_SIZE) == 0) || src->OperIsLocalRead() ||
- (src->OperIsIndir() && src->AsIndir()->Addr()->IsLocalAddrExpr()));
- if (size < XMM_REGSIZE_BYTES)
+ // chunks. As such, we'll only use this path for correctly-sized sources.
+ if ((loadSize < XMM_REGSIZE_BYTES) && ((loadSize % TARGET_POINTER_SIZE) == 0))
{
putArgStk->gtPutArgStkKind = GenTreePutArgStk::Kind::Push;
}
else
#endif // TARGET_X86
- if (size <= CPBLK_UNROLL_LIMIT)
+ if (loadSize <= CPBLK_UNROLL_LIMIT)
{
putArgStk->gtPutArgStkKind = GenTreePutArgStk::Kind::Unroll;
}
@@ -589,14 +593,25 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
#endif // !TARGET_X86
}
- // Always mark the OBJ and ADDR as contained trees by the putarg_stk. The codegen will deal with this tree.
- MakeSrcContained(putArgStk, src);
if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr())
{
- // If the source address is the address of a lclVar, make the source address contained to avoid
- // unnecessary copies.
- MakeSrcContained(putArgStk, src->AsObj()->Addr());
+ // TODO-ADDR: always perform this transformation in local morph and delete this code.
+ GenTreeLclVarCommon* lclAddrNode = src->AsObj()->Addr()->AsLclVarCommon();
+ BlockRange().Remove(lclAddrNode);
+
+ src->ChangeOper(GT_LCL_FLD);
+ src->AsLclFld()->SetLclNum(lclAddrNode->GetLclNum());
+ src->AsLclFld()->SetLclOffs(lclAddrNode->GetLclOffs());
+ src->AsLclFld()->SetLayout(layout);
}
+ else if (src->OperIs(GT_LCL_VAR))
+ {
+ comp->lvaSetVarDoNotEnregister(src->AsLclVar()->GetLclNum()
+ DEBUGARG(DoNotEnregisterReason::IsStructArg));
+ }
+
+ // Always mark the OBJ/LCL_VAR/LCL_FLD as contained trees.
+ MakeSrcContained(putArgStk, src);
}
else
{
@@ -604,13 +619,17 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
// so if possible, widen the load to avoid the sign/zero-extension.
if (varTypeIsSmall(regType) && srcIsLocal)
{
- assert(putArgStk->GetStackByteSize() <= genTypeSize(TYP_INT));
+ assert(genTypeSize(TYP_INT) <= putArgStk->GetStackByteSize());
regType = TYP_INT;
}
- src->SetOper(GT_IND);
src->ChangeType(regType);
- LowerIndir(src->AsIndir());
+
+ if (src->OperIs(GT_OBJ))
+ {
+ src->SetOper(GT_IND);
+ LowerIndir(src->AsIndir());
+ }
}
}
@@ -4056,6 +4075,93 @@ GenTree* Lowering::TryLowerAndOpToAndNot(GenTreeOp* andNode)
return andnNode;
}
+//----------------------------------------------------------------------------------------------
+// Lowering::TryLowerXorOpToGetMaskUpToLowestSetBit: Lowers a tree XOR(X, ADD(X, -1)) to
+// HWIntrinsic::GetMaskUpToLowestSetBit
+//
+// Arguments:
+// xorNode - GT_XOR node of integral type
+//
+// Return Value:
+// Returns the replacement node if one is created else nullptr indicating no replacement
+//
+// Notes:
+// Performs containment checks on the replacement node if one is created
+GenTree* Lowering::TryLowerXorOpToGetMaskUpToLowestSetBit(GenTreeOp* xorNode)
+{
+ assert(xorNode->OperIs(GT_XOR) && varTypeIsIntegral(xorNode));
+
+ GenTree* op1 = xorNode->gtGetOp1();
+ if (!op1->OperIs(GT_LCL_VAR) || comp->lvaGetDesc(op1->AsLclVar())->IsAddressExposed())
+ {
+ return nullptr;
+ }
+
+ GenTree* op2 = xorNode->gtGetOp2();
+ if (!op2->OperIs(GT_ADD))
+ {
+ return nullptr;
+ }
+
+ GenTree* addOp2 = op2->gtGetOp2();
+ if (!addOp2->IsIntegralConst(-1))
+ {
+ return nullptr;
+ }
+
+ GenTree* addOp1 = op2->gtGetOp1();
+ if (!addOp1->OperIs(GT_LCL_VAR) || (addOp1->AsLclVar()->GetLclNum() != op1->AsLclVar()->GetLclNum()))
+ {
+ return nullptr;
+ }
+
+ // Subsequent nodes may rely on CPU flags set by these nodes in which case we cannot remove them
+ if (((addOp2->gtFlags & GTF_SET_FLAGS) != 0) || ((op2->gtFlags & GTF_SET_FLAGS) != 0) ||
+ ((xorNode->gtFlags & GTF_SET_FLAGS) != 0))
+ {
+ return nullptr;
+ }
+
+ NamedIntrinsic intrinsic;
+ if (xorNode->TypeIs(TYP_LONG) && comp->compOpportunisticallyDependsOn(InstructionSet_BMI1_X64))
+ {
+ intrinsic = NamedIntrinsic::NI_BMI1_X64_GetMaskUpToLowestSetBit;
+ }
+ else if (comp->compOpportunisticallyDependsOn(InstructionSet_BMI1))
+ {
+ intrinsic = NamedIntrinsic::NI_BMI1_GetMaskUpToLowestSetBit;
+ }
+ else
+ {
+ return nullptr;
+ }
+
+ LIR::Use use;
+ if (!BlockRange().TryGetUse(xorNode, &use))
+ {
+ return nullptr;
+ }
+
+ GenTreeHWIntrinsic* blsmskNode = comp->gtNewScalarHWIntrinsicNode(xorNode->TypeGet(), op1, intrinsic);
+
+ JITDUMP("Lower: optimize XOR(X, ADD(X, -1)))\n");
+ DISPNODE(xorNode);
+ JITDUMP("to:\n");
+ DISPNODE(blsmskNode);
+
+ use.ReplaceWith(blsmskNode);
+
+ BlockRange().InsertBefore(xorNode, blsmskNode);
+ BlockRange().Remove(xorNode);
+ BlockRange().Remove(op2);
+ BlockRange().Remove(addOp1);
+ BlockRange().Remove(addOp2);
+
+ ContainCheckHWIntrinsic(blsmskNode);
+
+ return blsmskNode;
+}
+
//----------------------------------------------------------------------------------------------
// Lowering::LowerBswapOp: Tries to contain GT_BSWAP node when possible
//
@@ -4613,22 +4719,6 @@ void Lowering::ContainCheckCallOperands(GenTreeCall* call)
MakeSrcContained(call, ctrlExpr);
}
}
-
- for (CallArg& arg : call->gtArgs.EarlyArgs())
- {
- if (arg.GetEarlyNode()->OperIs(GT_PUTARG_STK))
- {
- LowerPutArgStk(arg.GetEarlyNode()->AsPutArgStk());
- }
- }
-
- for (CallArg& arg : call->gtArgs.LateArgs())
- {
- if (arg.GetLateNode()->OperIs(GT_PUTARG_STK))
- {
- LowerPutArgStk(arg.GetLateNode()->AsPutArgStk());
- }
- }
}
//------------------------------------------------------------------------
diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp
index 4a9eefef350c57..3d1cf8540a1423 100644
--- a/src/coreclr/jit/lsraarmarch.cpp
+++ b/src/coreclr/jit/lsraarmarch.cpp
@@ -403,20 +403,19 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode)
{
assert(argNode->gtOper == GT_PUTARG_STK);
- GenTree* putArgChild = argNode->gtGetOp1();
-
- int srcCount = 0;
+ GenTree* src = argNode->Data();
+ int srcCount = 0;
- // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
- if (putArgChild->TypeIs(TYP_STRUCT) || putArgChild->OperIs(GT_FIELD_LIST))
+ // Do we have a TYP_STRUCT argument, if so it must be a multireg pass-by-value struct
+ if (src->TypeIs(TYP_STRUCT))
{
// We will use store instructions that each write a register sized value
- if (putArgChild->OperIs(GT_FIELD_LIST))
+ if (src->OperIs(GT_FIELD_LIST))
{
- assert(putArgChild->isContained());
+ assert(src->isContained());
// We consume all of the items in the GT_FIELD_LIST
- for (GenTreeFieldList::Use& use : putArgChild->AsFieldList()->Uses())
+ for (GenTreeFieldList::Use& use : src->AsFieldList()->Uses())
{
BuildUse(use.GetNode());
srcCount++;
@@ -443,36 +442,25 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* argNode)
buildInternalIntRegisterDefForNode(argNode);
#endif // TARGET_ARM64
- if (putArgChild->OperGet() == GT_OBJ)
+ assert(src->isContained());
+
+ if (src->OperIs(GT_OBJ))
{
- assert(putArgChild->isContained());
- GenTree* objChild = putArgChild->gtGetOp1();
- if (objChild->OperGet() == GT_LCL_VAR_ADDR)
- {
- // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
- // as one contained operation, and there are no source registers.
- //
- assert(objChild->isContained());
- }
- else
- {
- // We will generate all of the code for the GT_PUTARG_STK and its child node
- // as one contained operation
- //
- srcCount = BuildOperandUses(objChild);
- }
+ // Build uses for the address to load from.
+ //
+ srcCount = BuildOperandUses(src->AsObj()->Addr());
}
else
{
// No source registers.
- putArgChild->OperIs(GT_LCL_VAR);
+ assert(src->OperIs(GT_LCL_VAR, GT_LCL_FLD));
}
}
}
else
{
- assert(!putArgChild->isContained());
- srcCount = BuildOperandUses(putArgChild);
+ assert(!src->isContained());
+ srcCount = BuildOperandUses(src);
#if defined(FEATURE_SIMD)
if (compMacOsArm64Abi() && argNode->GetStackByteSize() == 12)
{
@@ -820,13 +808,6 @@ int LinearScan::BuildCast(GenTreeCast* cast)
buildInternalFloatRegisterDefForNode(cast, RBM_ALLFLOAT);
setInternalRegsDelayFree = true;
}
-#else
- // Overflow checking cast from TYP_LONG to TYP_INT requires a temporary register to
- // store the min and max immediate values that cannot be encoded in the CMP instruction.
- if (cast->gtOverflow() && varTypeIsLong(srcType) && !cast->IsUnsigned() && (castType == TYP_INT))
- {
- buildInternalIntRegisterDefForNode(cast);
- }
#endif
int srcCount = BuildOperandUses(src);
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 5386ed5769558c..6ed5665febdc51 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -1561,21 +1561,31 @@ int LinearScan::BuildPutArgStk(GenTreePutArgStk* putArgStk)
return BuildOperandUses(src);
}
- ssize_t size = putArgStk->GetStackByteSize();
+ unsigned loadSize = putArgStk->GetArgLoadSize();
switch (putArgStk->gtPutArgStkKind)
{
case GenTreePutArgStk::Kind::Unroll:
// If we have a remainder smaller than XMM_REGSIZE_BYTES, we need an integer temp reg.
- if ((size % XMM_REGSIZE_BYTES) != 0)
+ if ((loadSize % XMM_REGSIZE_BYTES) != 0)
{
regMaskTP regMask = allRegs(TYP_INT);
+#ifdef TARGET_X86
+ // Storing at byte granularity requires a byteable register.
+ if ((loadSize & 1) != 0)
+ {
+ regMask &= allByteRegs();
+ }
+#endif // TARGET_X86
buildInternalIntRegisterDefForNode(putArgStk, regMask);
}
- if (size >= XMM_REGSIZE_BYTES)
+#ifdef TARGET_X86
+ if (loadSize >= 8)
+#else
+ if (loadSize >= XMM_REGSIZE_BYTES)
+#endif
{
- // If we have a buffer larger than or equal to XMM_REGSIZE_BYTES, reserve
- // an XMM register to use it for a series of 16-byte loads and stores.
+ // See "genStructPutArgUnroll" -- we will use this XMM register for wide stores.
buildInternalFloatRegisterDefForNode(putArgStk, internalFloatRegCandidates());
SetContainsAVXFlags();
}
@@ -2481,9 +2491,9 @@ int LinearScan::BuildCast(GenTreeCast* cast)
assert(!varTypeIsLong(srcType) || (src->OperIs(GT_LONG) && src->isContained()));
#else
- // Overflow checking cast from TYP_(U)LONG to TYP_UINT requires a temporary
+ // Overflow checking cast from TYP_(U)LONG to TYP_(U)INT requires a temporary
// register to extract the upper 32 bits of the 64 bit source register.
- if (cast->gtOverflow() && varTypeIsLong(srcType) && (castType == TYP_UINT))
+ if (cast->gtOverflow() && varTypeIsLong(srcType) && varTypeIsInt(castType))
{
// Here we don't need internal register to be different from targetReg,
// rather require it to be different from operand's reg.
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index ec5d602d5eb646..22fe9739444b43 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -179,16 +179,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
//
GenTree* Compiler::fgMorphExpandCast(GenTreeCast* tree)
{
- GenTree* oper = tree->CastOp();
-
- if (fgGlobalMorph && (oper->gtOper == GT_ADDR))
- {
- // Make sure we've checked if 'oper' is an address of an implicit-byref parameter.
- // If it is, fgMorphImplicitByRefArgs will change its type, and we want the cast
- // morphing code to see that type.
- fgMorphImplicitByRefArgs(oper);
- }
-
+ GenTree* oper = tree->CastOp();
var_types srcType = genActualType(oper);
var_types dstType = tree->CastToType();
unsigned dstSize = genTypeSize(dstType);
@@ -744,6 +735,12 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
unsigned argCount = CountArgs();
+ // Previous argument with GTF_EXCEPT
+ GenTree* prevExceptionTree = nullptr;
+ // Exceptions previous tree with GTF_EXCEPT may throw (computed lazily, may
+ // be empty)
+ ExceptionSetFlags prevExceptionFlags = ExceptionSetFlags::None;
+
for (CallArg& arg : Args())
{
GenTree* argx = arg.GetEarlyNode();
@@ -755,15 +752,14 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
continue;
}
+ bool canEvalToTemp = true;
if (arg.AbiInfo.GetRegNum() == REG_STK)
{
assert(m_hasStackArgs);
#if !FEATURE_FIXED_OUT_ARGS
- // On x86 we use push instructions to pass arguments:
- // The non-register arguments are evaluated and pushed in order
- // and they are never evaluated into temps
- //
- continue;
+ // Non-register arguments are evaluated and pushed in order; they
+ // should never go in the late arg list.
+ canEvalToTemp = false;
#endif
}
#if FEATURE_ARG_SPLIT
@@ -796,17 +792,20 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
if (argx->gtFlags & GTF_ASG)
{
- // If this is not the only argument, or it's a copyblk, or it already evaluates the expression to
- // a tmp, then we need a temp in the late arg list.
- if ((argCount > 1) || argx->OperIsCopyBlkOp()
-#ifdef FEATURE_FIXED_OUT_ARGS
- || arg.m_isTmp // Protect this by "FEATURE_FIXED_OUT_ARGS" to preserve the property
- // that we only have late non-register args when that feature is on.
-#endif
- )
+ // If this is not the only argument, or it's a copyblk, or it
+ // already evaluates the expression to a tmp then we need a temp in
+ // the late arg list.
+ // In the latter case this might not even be a value;
+ // fgMakeOutgoingStructArgCopy will leave the copying nodes here
+ // for FEATURE_FIXED_OUT_ARGS.
+ if (canEvalToTemp && ((argCount > 1) || argx->OperIsCopyBlkOp() || (FEATURE_FIXED_OUT_ARGS && arg.m_isTmp)))
{
SetNeedsTemp(&arg);
}
+ else
+ {
+ assert(argx->IsValue());
+ }
// For all previous arguments, unless they are a simple constant
// we require that they be evaluated into temps
@@ -817,6 +816,16 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
break;
}
+#if !FEATURE_FIXED_OUT_ARGS
+ if (prevArg.AbiInfo.GetRegNum() == REG_STK)
+ {
+ // All stack args are already evaluated and placed in order
+ // in this case; we only need to check this for register
+ // args.
+ break;
+ }
+#endif
+
if ((prevArg.GetEarlyNode() != nullptr) && !prevArg.GetEarlyNode()->IsInvariant())
{
SetNeedsTemp(&prevArg);
@@ -825,6 +834,8 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
}
bool treatLikeCall = ((argx->gtFlags & GTF_CALL) != 0);
+
+ ExceptionSetFlags exceptionFlags = ExceptionSetFlags::None;
#if FEATURE_FIXED_OUT_ARGS
// Like calls, if this argument has a tree that will do an inline throw,
// a call to a jit helper, then we need to treat it like a call (but only
@@ -833,43 +844,49 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
// conservative, but I want to avoid as much special-case debug-only code
// as possible, so leveraging the GTF_CALL flag is the easiest.
//
- if (!treatLikeCall && (argx->gtFlags & GTF_EXCEPT) && (argCount > 1) && comp->opts.compDbgCode &&
- (comp->fgWalkTreePre(&argx, Compiler::fgChkThrowCB) == Compiler::WALK_ABORT))
+ if (!treatLikeCall && (argx->gtFlags & GTF_EXCEPT) && (argCount > 1) && comp->opts.compDbgCode)
{
- for (CallArg& otherArg : Args())
+ exceptionFlags = comp->gtCollectExceptions(argx);
+ if ((exceptionFlags & (ExceptionSetFlags::IndexOutOfRangeException |
+ ExceptionSetFlags::OverflowException)) != ExceptionSetFlags::None)
{
- if (&otherArg == &arg)
+ for (CallArg& otherArg : Args())
{
- continue;
- }
+ if (&otherArg == &arg)
+ {
+ continue;
+ }
- if (otherArg.AbiInfo.GetRegNum() == REG_STK)
- {
- treatLikeCall = true;
- break;
+ if (otherArg.AbiInfo.GetRegNum() == REG_STK)
+ {
+ treatLikeCall = true;
+ break;
+ }
}
}
}
#endif // FEATURE_FIXED_OUT_ARGS
- /* If it contains a call (GTF_CALL) then itself and everything before the call
- with a GLOB_EFFECT must eval to temp (this is because everything with SIDE_EFFECT
- has to be kept in the right order since we will move the call to the first position)
+ // If it contains a call (GTF_CALL) then itself and everything before the call
+ // with a GLOB_EFFECT must eval to temp (this is because everything with SIDE_EFFECT
+ // has to be kept in the right order since we will move the call to the first position)
- For calls we don't have to be quite as conservative as we are with an assignment
- since the call won't be modifying any non-address taken LclVars.
- */
+ // For calls we don't have to be quite as conservative as we are with an assignment
+ // since the call won't be modifying any non-address taken LclVars.
if (treatLikeCall)
{
- if (argCount > 1) // If this is not the only argument
- {
- SetNeedsTemp(&arg);
- }
- else if (varTypeIsFloating(argx->TypeGet()) && (argx->OperGet() == GT_CALL))
+ if (canEvalToTemp)
{
- // Spill all arguments that are floating point calls
- SetNeedsTemp(&arg);
+ if (argCount > 1) // If this is not the only argument
+ {
+ SetNeedsTemp(&arg);
+ }
+ else if (varTypeIsFloating(argx->TypeGet()) && (argx->OperGet() == GT_CALL))
+ {
+ // Spill all arguments that are floating point calls
+ SetNeedsTemp(&arg);
+ }
}
// All previous arguments may need to be evaluated into temps
@@ -880,6 +897,15 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
break;
}
+#if !FEATURE_FIXED_OUT_ARGS
+ if (prevArg.AbiInfo.GetRegNum() == REG_STK)
+ {
+ // All stack args are already evaluated and placed in order
+ // in this case.
+ break;
+ }
+#endif
+
// For all previous arguments, if they have any GTF_ALL_EFFECT
// we require that they be evaluated into a temp
if ((prevArg.GetEarlyNode() != nullptr) && ((prevArg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0))
@@ -888,8 +914,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
}
#if FEATURE_FIXED_OUT_ARGS
// Or, if they are stored into the FIXED_OUT_ARG area
- // we require that they be moved to the gtCallLateArgs
- // and replaced with a placeholder node
+ // we require that they be moved to the late list
else if (prevArg.AbiInfo.GetRegNum() == REG_STK)
{
prevArg.m_needPlace = true;
@@ -903,6 +928,64 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
#endif
}
}
+ else if ((argx->gtFlags & GTF_EXCEPT) != 0)
+ {
+ // If a previous arg may throw a different exception than this arg
+ // then we evaluate all previous arguments with GTF_EXCEPT to temps
+ // to avoid reordering them in our sort later.
+ if (prevExceptionTree != nullptr)
+ {
+ if (prevExceptionFlags == ExceptionSetFlags::None)
+ {
+ prevExceptionFlags = comp->gtCollectExceptions(prevExceptionTree);
+ }
+
+ if (exceptionFlags == ExceptionSetFlags::None)
+ {
+ exceptionFlags = comp->gtCollectExceptions(argx);
+ }
+
+ bool exactlyOne = isPow2(static_cast(exceptionFlags));
+ bool throwsSameAsPrev = exactlyOne && (exceptionFlags == prevExceptionFlags);
+ if (!throwsSameAsPrev)
+ {
+ JITDUMP("Exception set for arg [%06u] interferes with previous tree [%06u]; must evaluate previous "
+ "trees with exceptions to temps\n",
+ Compiler::dspTreeID(argx), Compiler::dspTreeID(prevExceptionTree));
+
+ for (CallArg& prevArg : Args())
+ {
+ if (&prevArg == &arg)
+ {
+ break;
+ }
+
+#if !FEATURE_FIXED_OUT_ARGS
+ if (prevArg.AbiInfo.GetRegNum() == REG_STK)
+ {
+ // All stack args are already evaluated and placed in order
+ // in this case.
+ break;
+ }
+#endif
+ // Invariant here is that all nodes that were not
+ // already evaluated into temps and that throw can only
+ // be throwing the same single exception as the
+ // previous tree, so all of them interfere in the same
+ // way with the current arg and must be evaluated
+ // early.
+ if ((prevArg.GetEarlyNode() != nullptr) &&
+ ((prevArg.GetEarlyNode()->gtFlags & GTF_EXCEPT) != 0))
+ {
+ SetNeedsTemp(&prevArg);
+ }
+ }
+ }
+ }
+
+ prevExceptionTree = argx;
+ prevExceptionFlags = exceptionFlags;
+ }
#if FEATURE_MULTIREG_ARGS
// For RyuJIT backend we will expand a Multireg arg into a GT_FIELD_LIST
@@ -933,25 +1016,10 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
SetNeedsTemp(&arg);
}
#if defined(FEATURE_SIMD) && defined(TARGET_ARM64)
- else if (isMultiRegArg && varTypeIsSIMD(argx->TypeGet()))
+ else if (isMultiRegArg && varTypeIsSIMD(argx) && (argx->OperIsSimdOrHWintrinsic() || argx->IsCnsVec()))
{
- GenTree* nodeToCheck = argx;
-
- if (nodeToCheck->OperIs(GT_OBJ))
- {
- nodeToCheck = nodeToCheck->AsObj()->gtOp1;
-
- if (nodeToCheck->OperIs(GT_ADDR))
- {
- nodeToCheck = nodeToCheck->AsOp()->gtOp1;
- }
- }
-
- // SIMD types do not need the optimization below due to their sizes
- if (nodeToCheck->OperIsSimdOrHWintrinsic() || nodeToCheck->IsCnsVec())
- {
- SetNeedsTemp(&arg);
- }
+ // Multi-reg morphing does not handle these SIMD nodes.
+ SetNeedsTemp(&arg);
}
#endif
#ifndef TARGET_ARM
@@ -1122,12 +1190,7 @@ void CallArgs::SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs)
{
assert(m_argsComplete);
-#ifdef DEBUG
- if (comp->verbose)
- {
- printf("\nSorting the arguments:\n");
- }
-#endif
+ JITDUMP("\nSorting the arguments:\n");
// Shuffle the arguments around before we build the late args list. The
// idea is to move all "simple" arguments like constants and local vars to
@@ -1428,7 +1491,6 @@ GenTree* CallArgs::MakeTmpArgNode(Compiler* comp, CallArg* arg)
if (varTypeIsStruct(type))
{
-
#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64)
// Can this type be passed as a primitive type?
@@ -1500,21 +1562,8 @@ GenTree* CallArgs::MakeTmpArgNode(Compiler* comp, CallArg* arg)
#endif // !(TARGET_ARM64 || TARGET_LOONGARCH64)
#endif // FEATURE_MULTIREG_ARGS
}
-
-#else // not (TARGET_AMD64 or TARGET_ARM64 or TARGET_ARM or TARGET_LOONGARCH64)
-
- // other targets, we pass the struct by value
- assert(varTypeIsStruct(type));
-
- addrNode = comp->gtNewOperNode(GT_ADDR, TYP_BYREF, argNode);
-
- // Get a new Obj node temp to use it as a call argument.
- // gtNewObjNode will set the GTF_EXCEPT flag if this is not a local stack object.
- argNode = comp->gtNewObjNode(comp->lvaGetStruct(tmpVarNum), addrNode);
-
-#endif // not (TARGET_AMD64 or TARGET_ARM64 or TARGET_ARM or TARGET_LOONGARCH64)
-
- } // (varTypeIsStruct(type))
+#endif // (TARGET_AMD64 or TARGET_ARM64 or TARGET_ARM or TARGET_LOONGARCH64)
+ } // (varTypeIsStruct(type))
if (addrNode != nullptr)
{
@@ -1795,7 +1844,7 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call)
#ifdef DEBUG
if (comp->verbose)
{
- printf("\nShuffled argument table: ");
+ printf("\nRegister placement order: ");
for (CallArg& arg : LateArgs())
{
if (arg.AbiInfo.GetRegNum() != REG_STK)
@@ -2107,21 +2156,21 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
size_t addrValue = (size_t)call->gtEntryPoint.addr;
GenTree* indirectCellAddress = comp->gtNewIconHandleNode(addrValue, GTF_ICON_FTN_ADDR);
-#ifdef DEBUG
- indirectCellAddress->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd;
-#endif
- indirectCellAddress->SetRegNum(REG_R2R_INDIRECT_PARAM);
+ INDEBUG(indirectCellAddress->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd);
+
#ifdef TARGET_ARM
- // Issue #xxxx : Don't attempt to CSE this constant on ARM32
- //
- // This constant has specific register requirements, and LSRA doesn't currently correctly
- // handle them when the value is in a CSE'd local.
+ // TODO-ARM: We currently do not properly kill this register in LSRA
+ // (see getKillSetForCall which does so only for VSD calls).
+ // We should be able to remove these two workarounds once we do so,
+ // however when this was tried there were significant regressions.
+ indirectCellAddress->SetRegNum(REG_R2R_INDIRECT_PARAM);
indirectCellAddress->SetDoNotCSE();
-#endif // TARGET_ARM
+#endif
// Push the stub address onto the list of arguments.
- InsertAfterThisOrFirst(comp,
- NewCallArg::Primitive(indirectCellAddress).WellKnown(WellKnownArg::R2RIndirectionCell));
+ NewCallArg indirCellAddrArg =
+ NewCallArg::Primitive(indirectCellAddress).WellKnown(WellKnownArg::R2RIndirectionCell);
+ InsertAfterThisOrFirst(comp, indirCellAddrArg);
}
#endif
@@ -2235,7 +2284,12 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
argx->gtType = TYP_I_IMPL;
}
- // Setup any HFA information about 'argx'
+ // Note we must use the signature types for making ABI decisions. This is especially important for structs,
+ // where the "argx" node can legally have a type that is not ABI-compatible with the one in the signature.
+ const var_types argSigType = arg.GetSignatureType();
+ const CORINFO_CLASS_HANDLE argSigClass = arg.GetSignatureClassHandle();
+
+ // Setup any HFA information about the argument.
bool isHfaArg = false;
var_types hfaType = TYP_UNDEF;
unsigned hfaSlots = 0;
@@ -2247,7 +2301,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
if (GlobalJitOptions::compFeatureHfa)
{
- hfaType = comp->GetHfaType(argx);
+ hfaType = comp->GetHfaType(argSigClass);
isHfaArg = varTypeIsValidHfaType(hfaType);
#if defined(TARGET_ARM64)
@@ -2260,7 +2314,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
if (isHfaArg)
{
- hfaSlots = comp->GetHfaCount(argx);
+ hfaSlots = comp->GetHfaCount(argSigClass);
// If we have a HFA struct it's possible we transition from a method that originally
// only had integer types to now start having FP types. We have to communicate this
@@ -2274,11 +2328,19 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
const bool isFloatHfa = (hfaType == TYP_FLOAT);
#ifdef TARGET_ARM
- passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argx)) && !comp->opts.compUseSoftFP;
+ passUsingFloatRegs =
+ !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argSigType)) && !comp->opts.compUseSoftFP;
bool passUsingIntRegs = passUsingFloatRegs ? false : (intArgRegNum < MAX_REG_ARG);
- // We don't use the "size" return value from InferOpSizeAlign().
- comp->codeGen->InferOpSizeAlign(argx, &argAlignBytes);
+ // TODO-Cleanup: use "eeGetArgSizeAlignment" here. See also: https://github.com/dotnet/runtime/issues/46026.
+ if (varTypeIsStruct(argSigType))
+ {
+ argAlignBytes = comp->info.compCompHnd->getClassAlignmentRequirement(argSigClass);
+ }
+ else
+ {
+ argAlignBytes = genTypeSize(argSigType);
+ }
argAlignBytes = roundUp(argAlignBytes, TARGET_POINTER_SIZE);
@@ -2305,11 +2367,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
#elif defined(TARGET_ARM64)
assert(!callIsVararg || !isHfaArg);
- passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argx));
+ passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argSigType));
#elif defined(TARGET_AMD64)
- passUsingFloatRegs = varTypeIsFloating(argx);
+ passUsingFloatRegs = varTypeIsFloating(argSigType);
#elif defined(TARGET_X86)
@@ -2318,7 +2380,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
#elif defined(TARGET_LOONGARCH64)
assert(!callIsVararg && !isHfaArg);
- passUsingFloatRegs = varTypeUsesFloatReg(argx);
+ passUsingFloatRegs = varTypeUsesFloatReg(argSigType);
DWORD floatFieldFlags = STRUCT_NO_FLOAT_FIELD;
#else
@@ -2327,73 +2389,44 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
bool isBackFilled = false;
unsigned nextFltArgRegNum = fltArgRegNum; // This is the next floating-point argument register number to use
+ bool isStructArg = varTypeIsStruct(argSigType);
var_types structBaseType = TYP_STRUCT;
unsigned structSize = 0;
bool passStructByRef = false;
- bool isStructArg;
- GenTree* actualArg = argx->gtEffectiveVal(true /* Commas only */);
-
//
// Figure out the size of the argument. This is either in number of registers, or number of
// TARGET_POINTER_SIZE stack slots, or the sum of these if the argument is split between the registers and
// the stack.
//
- isStructArg = varTypeIsStruct(argx);
- // Note that we internally in the JIT can change some struct args to
- // primitive args (e.g. OBJ(x) -> IND(x)). Similarly,
- // the ABI type can also change from struct to primitive (e.g. a 8-byte
- // struct passed in a register). So isStructArg may be false even if
- // the signature type was (or is) a struct, however only in cases where
- // it does not matter.
- CORINFO_CLASS_HANDLE objClass = NO_CLASS_HANDLE;
+
if (isStructArg)
{
- objClass = comp->gtGetStructHandle(argx);
- if (argx->TypeGet() == TYP_STRUCT)
- {
- // For TYP_STRUCT arguments we must have an OBJ, LCL_VAR or MKREFANY
- switch (actualArg->OperGet())
- {
- case GT_OBJ:
- structSize = actualArg->AsObj()->GetLayout()->GetSize();
- assert(structSize == comp->info.compCompHnd->getClassSize(objClass));
- break;
- case GT_LCL_VAR:
- structSize = comp->lvaGetDesc(actualArg->AsLclVarCommon())->lvExactSize;
- break;
- case GT_MKREFANY:
- structSize = comp->info.compCompHnd->getClassSize(objClass);
- break;
- default:
- BADCODE("illegal argument tree: cannot determine size for ABI handling");
- break;
- }
- }
- else
- {
- structSize = genTypeSize(argx);
- assert(structSize == comp->info.compCompHnd->getClassSize(objClass));
- }
+ GenTree* actualArg = argx->gtEffectiveVal(true /* Commas only */);
+
+ // Here we look at "actualArg" to avoid calling "getClassSize".
+ structSize = actualArg->TypeIs(TYP_STRUCT) ? actualArg->GetLayout(comp)->GetSize() : genTypeSize(actualArg);
+
+ assert(structSize == comp->info.compCompHnd->getClassSize(argSigClass));
}
#if defined(TARGET_AMD64)
#ifdef UNIX_AMD64_ABI
if (!isStructArg)
{
size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot'
- byteSize = genTypeSize(arg.GetSignatureType());
+ byteSize = genTypeSize(argSigType);
}
else
{
size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE;
byteSize = structSize;
- comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(objClass, &structDesc);
+ comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(argSigClass, &structDesc);
}
#else // !UNIX_AMD64_ABI
size = 1; // On AMD64 Windows, all args fit in a single (64-bit) 'slot'
if (!isStructArg)
{
- byteSize = genTypeSize(arg.GetSignatureType());
+ byteSize = genTypeSize(argSigType);
}
#endif // UNIX_AMD64_ABI
@@ -2404,9 +2437,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
{
// HFA structs are passed by value in multiple registers.
// The "size" in registers may differ the size in pointer-sized units.
- CORINFO_CLASS_HANDLE structHnd = comp->gtGetStructHandle(argx);
- size = comp->GetHfaCount(structHnd);
- byteSize = comp->info.compCompHnd->getClassSize(structHnd);
+ size = hfaSlots;
+ byteSize = structSize;
}
else
{
@@ -2422,13 +2454,13 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
size = 1;
}
}
- // Note that there are some additional rules for multireg structs.
+ // Note that there are some additional rules for multireg structs on ARM64.
// (i.e they cannot be split between registers and the stack)
}
else
{
size = 1; // Otherwise, all primitive types fit in a single (64-bit) 'slot'
- byteSize = genTypeSize(arg.GetSignatureType());
+ byteSize = genTypeSize(argSigType);
}
#elif defined(TARGET_ARM) || defined(TARGET_X86)
if (isStructArg)
@@ -2440,8 +2472,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
{
// The typical case.
// Long/double type argument(s) will be modified as needed in Lowering.
- size = genTypeStSz(argx->gtType);
- byteSize = genTypeSize(arg.GetSignatureType());
+ size = genTypeStSz(argSigType);
+ byteSize = genTypeSize(argSigType);
}
#else
#error Unsupported or unset target architecture
@@ -2453,14 +2485,14 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
assert(structSize != 0);
Compiler::structPassingKind howToPassStruct;
- structBaseType = comp->getArgTypeForStruct(objClass, &howToPassStruct, callIsVararg, structSize);
+ structBaseType = comp->getArgTypeForStruct(argSigClass, &howToPassStruct, callIsVararg, structSize);
passStructByRef = (howToPassStruct == Compiler::SPK_ByReference);
#if defined(TARGET_LOONGARCH64)
if (!passStructByRef)
{
assert((howToPassStruct == Compiler::SPK_ByValue) || (howToPassStruct == Compiler::SPK_PrimitiveType));
- floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(objClass);
+ floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(argSigClass);
passUsingFloatRegs = (floatFieldFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) ? true : false;
comp->compFloatingPointUsed |= passUsingFloatRegs;
@@ -2471,8 +2503,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
// for "struct { float, float }", and retyping to a primitive here will cause the
// multi-reg morphing to not kick in (the struct in question needs to be passed in
// two FP registers). Here is just keep "structBaseType" as "TYP_STRUCT".
- // TODO-LoongArch64: fix "getPrimitiveTypeForStruct" or use the ABI information in
- // the arg entry instead of calling it here.
+ // TODO-LoongArch64: fix "getPrimitiveTypeForStruct".
structBaseType = TYP_STRUCT;
}
@@ -2531,7 +2562,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
// Arm64 Apple has a special ABI for passing small size arguments on stack,
// bytes are aligned to 1-byte, shorts to 2-byte, int/float to 4-byte, etc.
// It means passing 8 1-byte arguments on stack can take as small as 8 bytes.
- argAlignBytes = comp->eeGetArgSizeAlignment(arg.GetSignatureType(), isFloatHfa);
+ argAlignBytes = comp->eeGetArgSizeAlignment(argSigType, isFloatHfa);
}
#ifdef TARGET_LOONGARCH64
@@ -2543,11 +2574,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
bool isRegArg = false;
regNumber nonStdRegNum = REG_NA;
- if (isRegParamType(genActualType(argx->TypeGet()))
+ if (isRegParamType(genActualType(argSigType))
#ifdef UNIX_AMD64_ABI
&& (!isStructArg || structDesc.passedInRegisters)
#elif defined(TARGET_X86)
- || (isStructArg && comp->isTrivialPointerSizedStruct(objClass))
+ || (isStructArg && comp->isTrivialPointerSizedStruct(argSigClass))
#endif
)
{
@@ -3003,12 +3034,9 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
#endif
}
- if (GlobalJitOptions::compFeatureHfa)
+ if (isHfaArg)
{
- if (isHfaArg)
- {
- arg.AbiInfo.SetHfaType(hfaType, hfaSlots);
- }
+ arg.AbiInfo.SetHfaType(hfaType, hfaSlots);
}
arg.AbiInfo.SetMultiRegNums();
@@ -3199,15 +3227,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
unsigned originalSize;
if (argObj->TypeGet() == TYP_STRUCT)
{
- if (argObj->OperIs(GT_OBJ))
- {
- originalSize = argObj->AsObj()->Size();
- }
- else
- {
- // Must be LCL_VAR: we have a BADCODE assert for this in AddFinalArgsAndDetermineABIInfo.
- originalSize = lvaGetDesc(argObj->AsLclVar())->lvExactSize;
- }
+ assert(argObj->OperIs(GT_OBJ, GT_LCL_VAR, GT_LCL_FLD));
+ originalSize = argObj->GetLayout(this)->GetSize();
}
else
{
@@ -3408,13 +3429,28 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
makeOutArgCopy = true;
}
}
- else if (genTypeSize(varDsc->TypeGet()) != genTypeSize(structBaseType))
+ else if (genTypeSize(varDsc) != genTypeSize(structBaseType))
{
// Not a promoted struct, so just swizzle the type by using GT_LCL_FLD
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::SwizzleArg));
argObj->ChangeOper(GT_LCL_FLD);
argObj->gtType = structBaseType;
}
+ else if (varTypeUsesFloatReg(varDsc) != varTypeUsesFloatReg(structBaseType))
+ {
+ // Here we can see int <-> float, long <-> double, long <-> simd8 mismatches, due
+ // to the "OBJ(ADDR(LCL))" => "LCL" folding above. The latter case is handled in
+ // lowering, others we will handle here via swizzling.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef TARGET_AMD64
+ if (varDsc->TypeGet() != TYP_SIMD8)
+#endif // TARGET_AMD64
+ {
+ lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::SwizzleArg));
+ argObj->ChangeOper(GT_LCL_FLD);
+ argObj->gtType = structBaseType;
+ }
+ }
}
else if (argObj->OperIs(GT_LCL_FLD, GT_IND))
{
@@ -3437,36 +3473,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
assert(varTypeIsEnregisterable(argObj->TypeGet()) ||
(makeOutArgCopy && varTypeIsEnregisterable(structBaseType)));
}
-
-#if !defined(UNIX_AMD64_ABI) && !defined(TARGET_ARMARCH) && !defined(TARGET_LOONGARCH64)
- // TODO-CQ-XARCH: there is no need for a temp copy if we improve our code generation in
- // `genPutStructArgStk` for xarch like we did it for Arm/Arm64.
-
- // We still have a struct unless we converted the GT_OBJ into a GT_IND above...
- if (isHfaArg && passUsingFloatRegs)
- {
- }
- else if (structBaseType == TYP_STRUCT)
- {
- // If the valuetype size is not a multiple of TARGET_POINTER_SIZE,
- // we must copyblk to a temp before doing the obj to avoid
- // the obj reading memory past the end of the valuetype
- if (roundupSize > originalSize)
- {
- makeOutArgCopy = true;
-
- // There are a few special cases where we can omit using a CopyBlk
- // where we normally would need to use one.
-
- if (argObj->OperIs(GT_OBJ) &&
- argObj->AsObj()->gtGetOp1()->IsLocalAddrExpr() != nullptr) // Is the source a LclVar?
- {
- makeOutArgCopy = false;
- }
- }
- }
-
-#endif // !UNIX_AMD64_ABI
}
}
@@ -3800,15 +3806,15 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
#if FEATURE_MULTIREG_ARGS
// Examine 'arg' and setup argValue objClass and structSize
//
- const CORINFO_CLASS_HANDLE objClass = gtGetStructHandle(argNode);
- GenTree* argValue = argNode; // normally argValue will be arg, but see right below
- unsigned structSize = 0;
+ GenTree* argValue = argNode; // normally argValue will be arg, but see right below
+ ClassLayout* layout = nullptr;
+ unsigned structSize = 0;
if (argNode->OperGet() == GT_OBJ)
{
- GenTreeObj* argObj = argNode->AsObj();
- ClassLayout* objLayout = argObj->GetLayout();
- structSize = objLayout->GetSize();
+ GenTreeObj* argObj = argNode->AsObj();
+ layout = argObj->GetLayout();
+ structSize = layout->GetSize();
// If we have a GT_OBJ of a GT_ADDR then we set argValue to the child node of the GT_ADDR.
// TODO-ADDR: always perform this transformation in local morph and delete this code.
@@ -3820,36 +3826,31 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
if (location->OperIsLocalRead())
{
if (!location->OperIs(GT_LCL_VAR) ||
- !ClassLayout::AreCompatible(lvaGetDesc(location->AsLclVarCommon())->GetLayout(), objLayout))
+ !ClassLayout::AreCompatible(lvaGetDesc(location->AsLclVarCommon())->GetLayout(), layout))
{
unsigned lclOffset = location->AsLclVarCommon()->GetLclOffs();
location->ChangeType(argObj->TypeGet());
location->SetOper(GT_LCL_FLD);
location->AsLclFld()->SetLclOffs(lclOffset);
- location->AsLclFld()->SetLayout(objLayout);
+ location->AsLclFld()->SetLayout(layout);
}
argValue = location;
}
}
}
- else if (argNode->OperGet() == GT_LCL_VAR)
- {
- LclVarDsc* varDsc = lvaGetDesc(argNode->AsLclVarCommon());
- structSize = varDsc->lvExactSize;
- }
- else if (!argNode->TypeIs(TYP_STRUCT))
+ else if (argNode->TypeIs(TYP_STRUCT))
{
- structSize = genTypeSize(argNode);
+ assert(argNode->OperIsLocalRead());
+ layout = argNode->AsLclVarCommon()->GetLayout(this);
+ structSize = layout->GetSize();
}
else
{
- structSize = info.compCompHnd->getClassSize(objClass);
+ structSize = genTypeSize(argNode);
}
- assert(structSize == info.compCompHnd->getClassSize(objClass));
-
struct ArgElem
{
var_types Type; // The type to load into the register (can be small).
@@ -3873,8 +3874,11 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
else
{
assert(structSize <= MAX_ARG_REG_COUNT * TARGET_POINTER_SIZE);
- BYTE gcPtrs[MAX_ARG_REG_COUNT];
- info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
+ assert((layout != nullptr) || varTypeIsSIMD(argValue));
+
+ auto getSlotType = [layout](unsigned inx) {
+ return (layout != nullptr) ? layout->GetGCPtrType(inx) : TYP_I_IMPL;
+ };
// Here, we will set the sizes "rounded up" and then adjust the type of the last element below.
for (unsigned inx = 0, offset = 0; inx < elemCount; inx++)
@@ -3882,7 +3886,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
elems[inx].Offset = offset;
#if defined(UNIX_AMD64_ABI)
- if (gcPtrs[inx] == TYPE_GC_NONE)
+ if (!varTypeIsGC(getSlotType(inx)))
{
elems[inx].Type =
GetTypeFromClassificationAndSizes(arg->AbiInfo.StructDesc.eightByteClassifications[inx],
@@ -3899,7 +3903,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
else
#endif // TARGET_LOONGARCH64
{
- elems[inx].Type = getJitGCType(gcPtrs[inx]);
+ elems[inx].Type = getSlotType(inx);
offset += TARGET_POINTER_SIZE;
}
}
@@ -4053,11 +4057,10 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg)
}
else
{
- assert(argValue->OperIs(GT_OBJ));
+ assert(argValue->OperIsIndir());
- GenTreeObj* argObj = argValue->AsObj();
- GenTree* baseAddr = argObj->Addr();
- var_types addrType = baseAddr->TypeGet();
+ GenTree* baseAddr = argValue->AsIndir()->Addr();
+ var_types addrType = baseAddr->TypeGet();
// TODO-ADDR: make sure all such OBJs are transformed into TYP_STRUCT LCL_FLDs and delete this condition.
GenTreeLclVarCommon* lclSrcNode = baseAddr->IsLocalAddrExpr();
@@ -4158,7 +4161,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
//
// We don't need a copy if this is the last use of an implicit by-ref local.
//
- if (opts.OptimizationEnabled())
+ if (opts.OptimizationEnabled() && arg->AbiInfo.PassedByRef)
{
GenTreeLclVar* const lcl = argx->IsImplicitByrefParameterValue(this);
@@ -4220,7 +4223,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
{
tmp = (unsigned)lclNum;
found = true;
- JITDUMP("reusing outgoing struct arg");
+ JITDUMP("reusing outgoing struct arg\n");
break;
}
}
@@ -4267,7 +4270,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
// When on Unix create LCL_FLD for structs passed in more than one registers. See fgMakeTmpArgNode
GenTree* argNode = copyBlk;
-#else // FEATURE_FIXED_OUT_ARGS
+#else // !FEATURE_FIXED_OUT_ARGS
// Structs are always on the stack, and thus never need temps
// so we have to put the copy and temp all into one expression.
@@ -4276,7 +4279,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
// Change the expression to "(tmp=val),tmp"
argNode = gtNewOperNode(GT_COMMA, argNode->TypeGet(), copyBlk, argNode);
-#endif // FEATURE_FIXED_OUT_ARGS
+#endif // !FEATURE_FIXED_OUT_ARGS
arg->SetEarlyNode(argNode);
}
@@ -4642,10 +4645,11 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr)
// Likewise, allocate a temporary if the expression is a GT_LCL_FLD node. These used to be created
// after fgMorphIndexAddr from GT_FIELD trees so this preserves the existing behavior. This is
// perhaps a decision that should be left to CSE but FX diffs show that it is slightly better to
- // do this here.
+ // do this here. Likewise for implicit byrefs.
- if ((arrRef->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) ||
- gtComplexityExceeds(&arrRef, MAX_ARR_COMPLEXITY) || arrRef->OperIs(GT_FIELD, GT_LCL_FLD))
+ if (((arrRef->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) != 0) ||
+ gtComplexityExceeds(&arrRef, MAX_ARR_COMPLEXITY) || arrRef->OperIs(GT_FIELD, GT_LCL_FLD) ||
+ (arrRef->OperIs(GT_LCL_VAR) && lvaIsLocalImplicitlyAccessedByRef(arrRef->AsLclVar()->GetLclNum())))
{
unsigned arrRefTmpNum = lvaGrabTemp(true DEBUGARG("arr expr"));
arrRefDefn = gtNewTempAssign(arrRefTmpNum, arrRef);
@@ -4658,8 +4662,9 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr)
noway_assert(arrRef2 != nullptr);
}
- if ((index->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) || gtComplexityExceeds(&index, MAX_ARR_COMPLEXITY) ||
- index->OperIs(GT_FIELD, GT_LCL_FLD))
+ if (((index->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) != 0) ||
+ gtComplexityExceeds(&index, MAX_ARR_COMPLEXITY) || index->OperIs(GT_FIELD, GT_LCL_FLD) ||
+ (index->OperIs(GT_LCL_VAR) && lvaIsLocalImplicitlyAccessedByRef(index->AsLclVar()->GetLclNum())))
{
unsigned indexTmpNum = lvaGrabTemp(true DEBUGARG("index expr"));
indexDefn = gtNewTempAssign(indexTmpNum, index);
@@ -4765,8 +4770,8 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr)
// we at least will be able to hoist/CSE "index + elemOffset" in some cases.
// See https://github.com/dotnet/runtime/pull/61293#issuecomment-964146497
- // Use 2) form only for primitive types for now - it significantly reduced number of size regressions
- if (!varTypeIsIntegral(elemTyp) && !varTypeIsFloating(elemTyp))
+ // Don't use 2) for structs to reduce number of size regressions
+ if (varTypeIsStruct(elemTyp))
{
groupArrayRefWithElemOffset = false;
}
@@ -4847,11 +4852,15 @@ GenTree* Compiler::fgMorphLocal(GenTreeLclVarCommon* lclNode)
GenTree* expandedTree = nullptr;
#ifdef TARGET_X86
expandedTree = fgMorphExpandStackArgForVarArgs(lclNode);
-#endif // TARGET_X86
+#else
+ expandedTree = fgMorphExpandImplicitByRefArg(lclNode);
+#endif
if (expandedTree != nullptr)
{
- return fgMorphTree(expandedTree);
+ expandedTree = fgMorphTree(expandedTree);
+ DBEXEC(expandedTree == lclNode, expandedTree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED);
+ return expandedTree;
}
if (lclNode->OperIsLocalAddr())
@@ -4928,6 +4937,116 @@ GenTree* Compiler::fgMorphExpandStackArgForVarArgs(GenTreeLclVarCommon* lclNode)
}
#endif
+//------------------------------------------------------------------------
+// fgMorphExpandImplicitByRefArg: Morph an implicit by-ref parameter.
+//
+// Arguments:
+// lclNode - The local node to morph
+//
+// Return Value:
+// The expanded tree for "lclNode", which the caller is expected to
+// morph further.
+//
+GenTree* Compiler::fgMorphExpandImplicitByRefArg(GenTreeLclVarCommon* lclNode)
+{
+ if (!fgGlobalMorph)
+ {
+ return nullptr;
+ }
+
+ unsigned lclNum = lclNode->GetLclNum();
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
+ unsigned fieldOffset = 0;
+ unsigned newLclNum = BAD_VAR_NUM;
+
+ if (lvaIsImplicitByRefLocal(lclNum))
+ {
+ // The SIMD transformation to coalesce contiguous references to SIMD vector fields will re-invoke
+ // the traversal to mark address-taken locals. So, we may encounter a tree that has already been
+ // transformed to TYP_BYREF. If we do, leave it as-is.
+ if (lclNode->OperIs(GT_LCL_VAR) && lclNode->TypeIs(TYP_BYREF))
+ {
+ return nullptr;
+ }
+
+ if (varDsc->lvPromoted)
+ {
+ // fgRetypeImplicitByRefArgs created a new promoted struct local to represent this arg.
+ // Rewrite the node to refer to it.
+ assert(varDsc->lvFieldLclStart != 0);
+
+ lclNode->SetLclNum(varDsc->lvFieldLclStart);
+ return lclNode;
+ }
+
+ newLclNum = lclNum;
+ }
+ else if (varDsc->lvIsStructField && lvaIsImplicitByRefLocal(varDsc->lvParentLcl))
+ {
+ // This was a field reference to an implicit-by-reference struct parameter that was
+ // dependently promoted.
+ newLclNum = varDsc->lvParentLcl;
+ fieldOffset = varDsc->lvFldOffset;
+ }
+ else
+ {
+ return nullptr;
+ }
+
+ // Add a level of indirection to this node. The "base" will be a local node referring to "newLclNum".
+ // We will also add an offset, and, if the original "lclNode" represents a location, a dereference.
+ bool isAddress = lclNode->OperIsLocalAddr();
+ unsigned offset = lclNode->GetLclOffs() + fieldOffset;
+ var_types argNodeType = lclNode->TypeGet();
+ ClassLayout* argNodeLayout = nullptr;
+ if (varTypeIsStruct(argNodeType))
+ {
+ argNodeLayout = lclNode->GetLayout(this);
+ }
+
+ JITDUMP("\nRewriting an implicit by-ref parameter %s:\n", isAddress ? "address" : "reference");
+ DISPTREE(lclNode);
+
+ lclNode->ChangeType(TYP_BYREF);
+ lclNode->ChangeOper(GT_LCL_VAR);
+ lclNode->SetLclNum(newLclNum);
+ lclNode->SetAllEffectsFlags(GTF_EMPTY); // Implicit by-ref parameters cannot be address-exposed.
+
+ GenTree* addrNode = lclNode;
+ if (offset != 0)
+ {
+ addrNode = gtNewOperNode(GT_ADD, TYP_BYREF, addrNode, gtNewIconNode(offset, TYP_I_IMPL));
+ }
+
+ GenTree* newArgNode;
+ if (!isAddress)
+ {
+ if (varTypeIsStruct(argNodeType))
+ {
+ newArgNode = gtNewObjNode(argNodeLayout, addrNode);
+ }
+ else
+ {
+ newArgNode = gtNewIndir(argNodeType, addrNode);
+ }
+
+ // Currently, we have to conservatively treat all indirections off of implicit byrefs as
+ // global. This is because we lose the information on whether the original local's address
+ // was exposed when we retype it in "fgRetypeImplicitByRefArgs".
+ newArgNode->gtFlags |= GTF_GLOB_REF;
+ }
+ else
+ {
+ newArgNode = addrNode;
+ }
+
+ JITDUMP("Transformed into:\n");
+ DISPTREE(newArgNode);
+ JITDUMP("\n");
+
+ return newArgNode;
+}
+
/*****************************************************************************
*
* Transform the given GT_LCL_VAR tree for code generation.
@@ -5036,21 +5155,12 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
CORINFO_FIELD_HANDLE symHnd = tree->AsField()->gtFldHnd;
unsigned fldOffset = tree->AsField()->gtFldOffset;
GenTree* objRef = tree->AsField()->GetFldObj();
- bool objIsLocal = false;
bool fldMayOverlap = tree->AsField()->gtFldMayOverlap;
FieldSeqNode* fieldSeq = FieldSeqStore::NotAField();
// Reset the flag because we may reuse the node.
tree->AsField()->gtFldMayOverlap = false;
- if (fgGlobalMorph && (objRef != nullptr) && (objRef->gtOper == GT_ADDR))
- {
- // Make sure we've checked if 'objRef' is an address of an implicit-byref parameter.
- // If it is, fgMorphImplicitByRefArgs may change it do a different opcode, which the
- // simd field rewrites are sensitive to.
- fgMorphImplicitByRefArgs(objRef);
- }
-
noway_assert(((objRef != nullptr) && (objRef->IsLocalAddrExpr() != nullptr)) ||
((tree->gtFlags & GTF_GLOB_REF) != 0));
@@ -5082,13 +5192,9 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// before it is used.
MorphAddrContext defMAC(MACK_Ind);
- /* Is this an instance data member? */
-
- if (objRef)
+ // Is this an instance data member?
+ if (objRef != nullptr)
{
- GenTree* addr;
- objIsLocal = objRef->IsLocal();
-
if (tree->gtFlags & GTF_IND_TLS_REF)
{
NO_WAY("instance field can not be a TLS ref.");
@@ -5167,8 +5273,8 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
*/
var_types objRefType = objRef->TypeGet();
-
- GenTree* comma = nullptr;
+ GenTree* addr = nullptr;
+ GenTree* comma = nullptr;
// NULL mac means we encounter the GT_FIELD first. This denotes a dereference of the field,
// and thus is equivalent to a MACK_Ind with zero offset.
@@ -5243,11 +5349,10 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// Create the "comma" subtree
//
GenTree* asg = nullptr;
- GenTree* nullchk;
unsigned lclNum;
- if (objRef->gtOper != GT_LCL_VAR)
+ if (!objRef->OperIs(GT_LCL_VAR) || lvaIsLocalImplicitlyAccessedByRef(objRef->AsLclVar()->GetLclNum()))
{
lclNum = fgGetBigOffsetMorphingTemp(genActualType(objRef->TypeGet()));
@@ -5259,17 +5364,13 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
lclNum = objRef->AsLclVarCommon()->GetLclNum();
}
- GenTree* lclVar = gtNewLclvNode(lclNum, objRefType);
- nullchk = gtNewNullCheck(lclVar, compCurBB);
+ GenTree* lclVar = gtNewLclvNode(lclNum, objRefType);
+ GenTree* nullchk = gtNewNullCheck(lclVar, compCurBB);
- if (asg)
+ if (asg != nullptr)
{
// Create the "comma" node.
- comma = gtNewOperNode(GT_COMMA,
- TYP_VOID, // We don't want to return anything from this "comma" node.
- // Set the type to TYP_VOID, so we can select "cmp" instruction
- // instead of "mov" instruction later on.
- asg, nullchk);
+ comma = gtNewOperNode(GT_COMMA, TYP_VOID, asg, nullchk);
}
else
{
@@ -5494,7 +5595,8 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
{
handleKind = GTF_ICON_STATIC_HDL;
}
- GenTree* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq);
+ GenTreeIntCon* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq);
+ INDEBUG(addr->gtTargetHandle = reinterpret_cast(symHnd));
// Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS, if we need to.
if (((tree->gtFlags & GTF_FLD_INITCLASS) != 0) && !isStaticReadOnlyInited)
@@ -6649,9 +6751,11 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call)
(call->gtCallType == CT_USER_FUNC) ? call->gtCallMethHnd : nullptr,
call->IsTailPrefixedCall(), tailCallResult, nullptr);
- // Are we currently planning to expand the gtControlExpr as an early virtual call target?
+ // Do some profitability checks for whether we should expand a vtable call
+ // target early. Note that we may already have expanded it due to GDV at
+ // this point, so make sure we do not undo that work.
//
- if (call->IsExpandedEarly() && call->IsVirtualVtable())
+ if (call->IsExpandedEarly() && call->IsVirtualVtable() && (call->gtControlExpr == nullptr))
{
assert(call->gtArgs.HasThisPointer());
// It isn't alway profitable to expand a virtual call early
@@ -7918,7 +8022,7 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call)
// call - a call that needs virtual stub dispatching.
//
// Return Value:
-// addr tree with set resister requirements.
+// addr tree
//
GenTree* Compiler::fgGetStubAddrArg(GenTreeCall* call)
{
@@ -7933,12 +8037,9 @@ GenTree* Compiler::fgGetStubAddrArg(GenTreeCall* call)
assert(call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT);
ssize_t addr = ssize_t(call->gtStubCallStubAddr);
stubAddrArg = gtNewIconHandleNode(addr, GTF_ICON_FTN_ADDR);
-#ifdef DEBUG
- stubAddrArg->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd;
-#endif
+ INDEBUG(stubAddrArg->AsIntCon()->gtTargetHandle = (size_t)call->gtCallMethHnd);
}
assert(stubAddrArg != nullptr);
- stubAddrArg->SetRegNum(virtualStubParamInfo->GetReg());
return stubAddrArg;
}
@@ -8425,18 +8526,18 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
//
if (call->IsExpandedEarly() && call->IsVirtualVtable())
{
- // We only expand the Vtable Call target once in the global morph phase
- if (fgGlobalMorph)
+ // We expand the Vtable Call target either in the global morph phase or
+ // in guarded devirt if we need it for the guard.
+ if (fgGlobalMorph && (call->gtControlExpr == nullptr))
{
- assert(call->gtControlExpr == nullptr); // We only call this method and assign gtControlExpr once
call->gtControlExpr = fgExpandVirtualVtableCallTarget(call);
}
// We always have to morph or re-morph the control expr
//
call->gtControlExpr = fgMorphTree(call->gtControlExpr);
- // Propagate any gtFlags into the call
- call->gtFlags |= call->gtControlExpr->gtFlags;
+ // Propagate any side effect flags into the call
+ call->gtFlags |= call->gtControlExpr->gtFlags & GTF_ALL_EFFECT;
}
// Morph stelem.ref helper call to store a null value, into a store into an array without the helper.
@@ -8822,7 +8923,8 @@ GenTree* Compiler::fgMorphLeaf(GenTree* tree)
// target of a Delegate or a raw function pointer.
bool isUnsafeFunctionPointer = !fptrValTree->gtFptrDelegateTarget;
- CORINFO_CONST_LOOKUP addrInfo;
+ CORINFO_CONST_LOOKUP addrInfo;
+ CORINFO_METHOD_HANDLE funcHandle = fptrValTree->gtFptrMethod;
#ifdef FEATURE_READYTORUN
if (fptrValTree->gtEntryPoint.addr != nullptr)
@@ -8832,7 +8934,7 @@ GenTree* Compiler::fgMorphLeaf(GenTree* tree)
else
#endif
{
- info.compCompHnd->getFunctionFixedEntryPoint(fptrValTree->gtFptrMethod, isUnsafeFunctionPointer, &addrInfo);
+ info.compCompHnd->getFunctionFixedEntryPoint(funcHandle, isUnsafeFunctionPointer, &addrInfo);
}
GenTree* indNode = nullptr;
@@ -8851,6 +8953,7 @@ GenTree* Compiler::fgMorphLeaf(GenTree* tree)
case IAT_PVALUE:
indNode = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)addrInfo.handle, GTF_ICON_FTN_ADDR, true);
+ INDEBUG(indNode->gtGetOp1()->AsIntCon()->gtTargetHandle = reinterpret_cast(funcHandle));
break;
case IAT_VALUE:
@@ -8859,6 +8962,7 @@ GenTree* Compiler::fgMorphLeaf(GenTree* tree)
tree->SetOper(GT_CNS_INT);
tree->AsIntConCommon()->SetIconValue(ssize_t(addrInfo.handle));
tree->gtFlags |= GTF_ICON_FTN_ADDR;
+ INDEBUG(tree->AsIntCon()->gtTargetHandle = reinterpret_cast(funcHandle));
break;
default:
@@ -9621,6 +9725,11 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, ClassLa
{
lclNode = effectiveVal->AsLclVarCommon();
}
+ else if (effectiveVal->OperIs(GT_LCL_FLD))
+ {
+ needsIndirection = false;
+ assert(ClassLayout::AreCompatible(effectiveVal->AsLclFld()->GetLayout(), blockLayout));
+ }
else if (effectiveVal->IsCall())
{
needsIndirection = false;
@@ -9869,7 +9978,7 @@ GenTree* Compiler::fgMorphFieldToSimdGetElement(GenTree* tree)
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
GenTree* op2 = gtNewIconNode(index, TYP_INT);
- assert(simdSize <= 16);
+ assert(simdSize <= 32);
assert(simdSize >= ((index + 1) * genTypeSize(simdBaseType)));
#if defined(TARGET_XARCH)
@@ -9944,7 +10053,7 @@ GenTree* Compiler::fgMorphFieldAssignToSimdSetElement(GenTree* tree)
var_types simdType = simdStructNode->gtType;
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(simdSize <= 16);
+ assert(simdSize <= 32);
assert(simdSize >= ((index + 1) * genTypeSize(simdBaseType)));
GenTree* op2 = gtNewIconNode(index, TYP_INT);
@@ -9960,18 +10069,6 @@ GenTree* Compiler::fgMorphFieldAssignToSimdSetElement(GenTree* tree)
tree->AsOp()->gtOp1 = target;
tree->AsOp()->gtOp2 = simdTree;
- // fgMorphTree has already called fgMorphImplicitByRefArgs() on this assignment, but the source
- // and target have not yet been morphed.
- // Therefore, in case the source and/or target are now implicit byrefs, we need to call it again.
- if (fgMorphImplicitByRefArgs(tree))
- {
- if (tree->gtGetOp1()->OperIsBlk())
- {
- assert(tree->gtGetOp1()->TypeGet() == simdType);
- tree->gtGetOp1()->SetOper(GT_IND);
- tree->gtGetOp1()->gtType = simdType;
- }
- }
#ifdef DEBUG
tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
@@ -10284,22 +10381,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
case GT_MUL:
noway_assert(op2 != nullptr);
- if (opts.OptimizationEnabled() && !optValnumCSE_phase && !tree->gtOverflow())
- {
- // MUL(NEG(a), C) => MUL(a, NEG(C))
- if (op1->OperIs(GT_NEG) && !op1->gtGetOp1()->IsCnsIntOrI() && op2->IsCnsIntOrI() &&
- !op2->IsIconHandle())
- {
- GenTree* newOp1 = op1->gtGetOp1();
- GenTree* newConst = gtNewIconNode(-op2->AsIntCon()->IconValue(), op2->TypeGet());
- DEBUG_DESTROY_NODE(op1);
- DEBUG_DESTROY_NODE(op2);
- tree->AsOp()->gtOp1 = newOp1;
- tree->AsOp()->gtOp2 = newConst;
- return fgMorphSmpOp(tree, mac);
- }
- }
-
#ifndef TARGET_64BIT
if (typ == TYP_LONG)
{
@@ -10560,10 +10641,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
#ifdef TARGET_ARM64
// ARM64 architecture manual suggests this transformation
// for the mod operator.
- // However, we do skip this optimization for ARM64 if the second operand
- // is an integral constant power of 2 because there is an even better
- // optimization in lowering that is specific for ARM64.
- else if (!(tree->OperIs(GT_MOD) && op2->IsIntegralConstPow2()))
+ else
#else
// XARCH only applies this transformation if we know
// that magic division will be used - which is determined
@@ -11213,9 +11291,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
*/
GenTree* temp;
- size_t ival1;
GenTree* lclVarTree;
- GenTree* effectiveOp1;
FieldSeqNode* fieldSeq = nullptr;
switch (oper)
@@ -11235,28 +11311,14 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
lclVarTree->gtFlags |= GTF_VAR_DEF;
}
- effectiveOp1 = op1->gtEffectiveVal();
-
- // If we are storing a small type, we might be able to omit a cast.
- if ((effectiveOp1->OperIs(GT_IND) ||
- (effectiveOp1->OperIs(GT_LCL_VAR) &&
- lvaGetDesc(effectiveOp1->AsLclVarCommon()->GetLclNum())->lvNormalizeOnLoad())) &&
- varTypeIsSmall(effectiveOp1))
+ if (op2->OperIs(GT_CAST))
{
- if (!gtIsActiveCSE_Candidate(op2) && op2->OperIs(GT_CAST) &&
- varTypeIsIntegral(op2->AsCast()->CastOp()) && !op2->gtOverflow())
- {
- var_types castType = op2->CastToType();
+ tree = fgOptimizeCastOnAssignment(tree->AsOp());
- // If we are performing a narrowing cast and
- // castType is larger or the same as op1's type
- // then we can discard the cast.
+ assert(tree->OperIs(GT_ASG));
- if (varTypeIsSmall(castType) && (genTypeSize(castType) >= genTypeSize(effectiveOp1)))
- {
- tree->AsOp()->gtOp2 = op2 = op2->AsCast()->CastOp();
- }
- }
+ op1 = tree->gtGetOp1();
+ op2 = tree->gtGetOp2();
}
fgAssignSetVarDef(tree);
@@ -11323,6 +11385,21 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
assert(op2 == tree->AsOp()->gtGetOp2());
}
+ if (opts.OptimizationEnabled() && fgGlobalMorph)
+ {
+ if (op2->IsIntegralConst() || op1->IsIntegralConst())
+ {
+ if (tree->OperIs(GT_GT, GT_LT, GT_LE, GT_GE))
+ {
+ tree = fgOptimizeRelationalComparisonWithFullRangeConst(tree->AsOp());
+ if (tree->OperIs(GT_CNS_INT))
+ {
+ return tree;
+ }
+ }
+ }
+ }
+
COMPARE:
noway_assert(tree->OperIsCompare());
@@ -11590,15 +11667,14 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
bool foldAndReturnTemp = false;
temp = nullptr;
- ival1 = 0;
// Don't remove a volatile GT_IND, even if the address points to a local variable.
- if ((tree->gtFlags & GTF_IND_VOLATILE) == 0)
+ //
+ if (!tree->AsIndir()->IsVolatile())
{
/* Try to Fold *(&X) into X */
if (op1->gtOper == GT_ADDR)
{
- // Can not remove a GT_ADDR if it is currently a CSE candidate.
if (gtIsActiveCSE_Candidate(op1))
{
break;
@@ -11698,98 +11774,34 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
}
}
#endif // TARGET_ARM
-
- /* Try to change *(&lcl + cns) into lcl[cns] to prevent materialization of &lcl */
-
- if (op1->AsOp()->gtOp1->OperGet() == GT_ADDR && op1->AsOp()->gtOp2->OperGet() == GT_CNS_INT &&
- opts.OptimizationEnabled())
- {
- // No overflow arithmetic with pointers
- noway_assert(!op1->gtOverflow());
-
- temp = op1->AsOp()->gtOp1->AsOp()->gtOp1;
- if (!temp->OperIsLocal())
- {
- temp = nullptr;
- break;
- }
-
- // Can not remove the GT_ADDR if it is currently a CSE candidate.
- if (gtIsActiveCSE_Candidate(op1->AsOp()->gtOp1))
- {
- break;
- }
-
- ival1 = op1->AsOp()->gtOp2->AsIntCon()->gtIconVal;
- fieldSeq = op1->AsOp()->gtOp2->AsIntCon()->gtFieldSeq;
-
- if (ival1 == 0 && typ == temp->TypeGet() && temp->TypeGet() != TYP_STRUCT)
- {
- noway_assert(!varTypeIsGC(temp->TypeGet()));
- foldAndReturnTemp = true;
- }
- else
- {
- // The emitter can't handle large offsets
- if (ival1 != (unsigned short)ival1)
- {
- break;
- }
-
- // The emitter can get confused by invalid offsets
- if (ival1 >= Compiler::lvaLclSize(temp->AsLclVarCommon()->GetLclNum()))
- {
- break;
- }
- }
- // Now we can fold this into a GT_LCL_FLD below
- // where we check (temp != nullptr)
- }
}
}
- // At this point we may have a lclVar or lclFld that might be foldable with a bit of extra massaging:
- // - We may have a load of a local where the load has a different type than the local
- // - We may have a load of a local plus an offset
- //
- // In these cases, we will change the lclVar or lclFld into a lclFld of the appropriate type and
- // offset if doing so is legal. The only cases in which this transformation is illegal are if the load
- // begins before the local or if the load extends beyond the end of the local (i.e. if the load is
- // out-of-bounds w.r.t. the local).
+ // At this point we may have a lclVar or lclFld of some mismatched type. In this case, we will change
+ // the lclVar or lclFld into a lclFld of the appropriate type if doing so is legal. The only cases in
+ // which this transformation is illegal is when we have a STRUCT indirection, as we do not have the
+ // necessary layout information, or if the load would extend beyond the local.
if ((temp != nullptr) && !foldAndReturnTemp)
{
- assert(temp->OperIsLocal());
+ assert(temp->OperIs(GT_LCL_VAR, GT_LCL_FLD));
- unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
+ unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
+ unsigned lclOffs = temp->AsLclVarCommon()->GetLclOffs();
// Make sure we do not enregister this lclVar.
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LocalField));
- // If the size of the load is greater than the size of the lclVar, we cannot fold this access into
- // a lclFld: the access represented by an lclFld node must begin at or after the start of the
- // lclVar and must not extend beyond the end of the lclVar.
- if ((ival1 >= 0) && ((ival1 + genTypeSize(typ)) <= lvaLclExactSize(lclNum)))
+ if ((typ != TYP_STRUCT) && ((lclOffs + genTypeSize(typ)) <= lvaLclExactSize(lclNum)))
{
- GenTreeLclFld* lclFld;
-
- // We will turn a GT_LCL_VAR into a GT_LCL_FLD with an gtLclOffs of 'ival'
- // or if we already have a GT_LCL_FLD we will adjust the gtLclOffs by adding 'ival'
- // Then we change the type of the GT_LCL_FLD to match the orginal GT_IND type.
+ // We will change the type of the node to match the orginal GT_IND type.
//
- if (temp->OperGet() == GT_LCL_FLD)
- {
- lclFld = temp->AsLclFld();
- lclFld->SetLclOffs(lclFld->GetLclOffs() + static_cast(ival1));
- }
- else // We have a GT_LCL_VAR.
+ temp->gtType = typ;
+
+ if (temp->OperIs(GT_LCL_VAR))
{
- assert(temp->OperGet() == GT_LCL_VAR);
- temp->ChangeOper(GT_LCL_FLD); // Note that this makes the gtFieldSeq "NotAField".
- lclFld = temp->AsLclFld();
- lclFld->SetLclOffs(static_cast(ival1));
+ temp->ChangeOper(GT_LCL_FLD);
}
- temp->gtType = tree->gtType;
foldAndReturnTemp = true;
}
}
@@ -11798,7 +11810,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
{
assert(temp != nullptr);
assert(temp->TypeGet() == typ);
- assert((op1->OperGet() == GT_ADD) || (op1->OperGet() == GT_ADDR));
+ assert(op1->OperIs(GT_ADDR));
// Copy the value of GTF_DONT_CSE from the original tree to `temp`: it can be set for
// 'temp' because a GT_ADDR always marks it for its operand.
@@ -11806,12 +11818,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
temp->gtFlags |= (tree->gtFlags & GTF_DONT_CSE);
temp->SetVNsFromNode(tree);
- if (op1->OperGet() == GT_ADD)
- {
- DEBUG_DESTROY_NODE(op1->AsOp()->gtOp1); // GT_ADDR
- DEBUG_DESTROY_NODE(op1->AsOp()->gtOp2); // GT_CNS_INT
- }
- DEBUG_DESTROY_NODE(op1); // GT_ADD or GT_ADDR
+ DEBUG_DESTROY_NODE(op1); // GT_ADDR
DEBUG_DESTROY_NODE(tree); // GT_IND
// If the result of the fold is a local var, we may need to perform further adjustments e.g. for
@@ -12293,6 +12300,75 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast)
return cast;
}
+//------------------------------------------------------------------------
+// fgOptimizeCastOnAssignment: Optimizes the supplied GT_ASG tree with a GT_CAST node.
+//
+// Arguments:
+// tree - the cast tree to optimize
+//
+// Return Value:
+// The optimized tree (must be GT_ASG).
+//
+GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg)
+{
+ assert(asg->OperIs(GT_ASG));
+
+ GenTree* const op1 = asg->gtGetOp1();
+ GenTree* const op2 = asg->gtGetOp2();
+
+ assert(op2->OperIs(GT_CAST));
+
+ GenTree* const effectiveOp1 = op1->gtEffectiveVal();
+
+ if (!effectiveOp1->OperIs(GT_IND, GT_LCL_VAR))
+ return asg;
+
+ if (effectiveOp1->OperIs(GT_LCL_VAR) &&
+ !lvaGetDesc(effectiveOp1->AsLclVarCommon()->GetLclNum())->lvNormalizeOnLoad())
+ return asg;
+
+ if (op2->gtOverflow())
+ return asg;
+
+ if (gtIsActiveCSE_Candidate(op2))
+ return asg;
+
+ GenTreeCast* cast = op2->AsCast();
+ var_types castToType = cast->CastToType();
+ var_types castFromType = cast->CastFromType();
+
+ if (gtIsActiveCSE_Candidate(cast->CastOp()))
+ return asg;
+
+ if (!varTypeIsSmall(effectiveOp1))
+ return asg;
+
+ if (!varTypeIsSmall(castToType))
+ return asg;
+
+ if (!varTypeIsIntegral(castFromType))
+ return asg;
+
+ // If we are performing a narrowing cast and
+ // castToType is larger or the same as op1's type
+ // then we can discard the cast.
+ if (genTypeSize(castToType) < genTypeSize(effectiveOp1))
+ return asg;
+
+ if (genActualType(castFromType) == genActualType(castToType))
+ {
+ // Removes the cast.
+ asg->gtOp2 = cast->CastOp();
+ }
+ else
+ {
+ // This is a type-changing cast so we cannot remove it entirely.
+ cast->gtCastType = genActualType(castToType);
+ }
+
+ return asg;
+}
+
//------------------------------------------------------------------------
// fgOptimizeEqualityComparisonWithConst: optimizes various EQ/NE(OP, CONST) patterns.
//
@@ -12529,6 +12605,123 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp)
return cmp;
}
+//------------------------------------------------------------------------
+// fgOptimizeRelationalComparisonWithFullRangeConst: optimizes a comparison operation.
+//
+// Recognizes "Always false"/"Always true" comparisons against various full range constant operands and morphs
+// them into zero/one.
+//
+// Arguments:
+// cmp - the GT_LT/GT_GT tree to morph.
+//
+// Return Value:
+// 1. The unmodified "cmp" tree.
+// 2. A CNS_INT node containing zero.
+// 3. A CNS_INT node containing one.
+// Assumptions:
+// The second operand is an integral constant or the first operand is an integral constant.
+//
+GenTree* Compiler::fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* cmp)
+{
+ if (gtTreeHasSideEffects(cmp, GTF_SIDE_EFFECT))
+ {
+ return cmp;
+ }
+
+ int64_t lhsMin;
+ int64_t lhsMax;
+ if (cmp->gtGetOp1()->IsIntegralConst())
+ {
+ lhsMin = cmp->gtGetOp1()->AsIntConCommon()->IntegralValue();
+ lhsMax = lhsMin;
+ }
+ else
+ {
+ IntegralRange lhsRange = IntegralRange::ForNode(cmp->gtGetOp1(), this);
+ lhsMin = IntegralRange::SymbolicToRealValue(lhsRange.GetLowerBound());
+ lhsMax = IntegralRange::SymbolicToRealValue(lhsRange.GetUpperBound());
+ }
+
+ int64_t rhsMin;
+ int64_t rhsMax;
+ if (cmp->gtGetOp2()->IsIntegralConst())
+ {
+ rhsMin = cmp->gtGetOp2()->AsIntConCommon()->IntegralValue();
+ rhsMax = rhsMin;
+ }
+ else
+ {
+ IntegralRange rhsRange = IntegralRange::ForNode(cmp->gtGetOp2(), this);
+ rhsMin = IntegralRange::SymbolicToRealValue(rhsRange.GetLowerBound());
+ rhsMax = IntegralRange::SymbolicToRealValue(rhsRange.GetUpperBound());
+ }
+
+ genTreeOps op = cmp->gtOper;
+ if ((op != GT_LT) && (op != GT_LE))
+ {
+ op = GenTree::SwapRelop(op);
+ std::swap(lhsMin, rhsMin);
+ std::swap(lhsMax, rhsMax);
+ }
+
+ GenTree* ret = nullptr;
+
+ if (cmp->IsUnsigned())
+ {
+ if ((lhsMin < 0) && (lhsMax >= 0))
+ {
+ // [0, (uint64_t)lhsMax] U [(uint64_t)lhsMin, MaxValue]
+ lhsMin = 0;
+ lhsMax = -1;
+ }
+
+ if ((rhsMin < 0) && (rhsMax >= 0))
+ {
+ // [0, (uint64_t)rhsMax] U [(uint64_t)rhsMin, MaxValue]
+ rhsMin = 0;
+ rhsMax = -1;
+ }
+
+ if (((op == GT_LT) && ((uint64_t)lhsMax < (uint64_t)rhsMin)) ||
+ ((op == GT_LE) && ((uint64_t)lhsMax <= (uint64_t)rhsMin)))
+ {
+ ret = gtNewOneConNode(TYP_INT);
+ }
+ else if (((op == GT_LT) && ((uint64_t)lhsMin >= (uint64_t)rhsMax)) ||
+ ((op == GT_LE) && ((uint64_t)lhsMin > (uint64_t)rhsMax)))
+ {
+ ret = gtNewZeroConNode(TYP_INT);
+ }
+ }
+ else
+ {
+ // [x0, x1] < [y0, y1] is false if x0 >= y1
+ // [x0, x1] <= [y0, y1] is false if x0 > y1
+ if (((op == GT_LT) && (lhsMin >= rhsMax)) || (((op == GT_LE) && (lhsMin > rhsMax))))
+ {
+ ret = gtNewZeroConNode(TYP_INT);
+ }
+ // [x0, x1] < [y0, y1] is true if x1 < y0
+ else if ((op == GT_LT) && (lhsMax < rhsMin))
+ {
+ ret = gtNewOneConNode(TYP_INT);
+ }
+ }
+
+ if (ret != nullptr)
+ {
+ fgUpdateConstTreeValueNumber(ret);
+
+ DEBUG_DESTROY_NODE(cmp);
+
+ INDEBUG(ret->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
+
+ return ret;
+ }
+
+ return cmp;
+}
+
//------------------------------------------------------------------------
// fgOptimizeRelationalComparisonWithConst: optimizes a comparison operation.
//
@@ -12813,9 +13006,39 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add)
return op1;
}
- // Note that these transformations are legal for floating-point ADDs as well.
if (opts.OptimizationEnabled())
{
+ // Reduce local addresses: "ADD(ADDR(LCL_VAR), OFFSET)" => "ADDR(LCL_FLD OFFSET)".
+ // TODO-ADDR: do "ADD(LCL_FLD/VAR_ADDR, OFFSET)" => "LCL_FLD_ADDR" instead.
+ //
+ if (op1->OperIs(GT_ADDR) && op2->IsCnsIntOrI() && op1->gtGetOp1()->OperIsLocalRead())
+ {
+ GenTreeUnOp* addrNode = op1->AsUnOp();
+ GenTreeLclVarCommon* lclNode = addrNode->gtGetOp1()->AsLclVarCommon();
+ GenTreeIntCon* offsetNode = op2->AsIntCon();
+ if (FitsIn(offsetNode->IconValue()))
+ {
+ unsigned offset = lclNode->GetLclOffs() + static_cast(offsetNode->IconValue());
+
+ // Note: the emitter does not expect out-of-bounds access for LCL_FLD_ADDR.
+ if (FitsIn(offset) && (offset < lvaLclExactSize(lclNode->GetLclNum())))
+ {
+ // Types of location nodes under ADDRs do not matter. We arbitrarily choose TYP_UBYTE.
+ lclNode->ChangeType(TYP_UBYTE);
+ lclNode->SetOper(GT_LCL_FLD);
+ lclNode->AsLclFld()->SetLclOffs(offset);
+ lvaSetVarDoNotEnregister(lclNode->GetLclNum() DEBUGARG(DoNotEnregisterReason::LocalField));
+
+ addrNode->SetVNsFromNode(add);
+
+ DEBUG_DESTROY_NODE(offsetNode);
+ DEBUG_DESTROY_NODE(add);
+
+ return addrNode;
+ }
+ }
+ }
+
// - a + b = > b - a
// ADD((NEG(a), b) => SUB(b, a)
@@ -12910,6 +13133,20 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul)
if (op2->IsIntegralConst())
{
+ // We should not get here for 64-bit multiplications on 32-bit.
+ assert(op2->IsCnsIntOrI());
+
+ // MUL(NEG(a), C) => MUL(a, NEG(C))
+ if (opts.OptimizationEnabled() && op1->OperIs(GT_NEG) && !op2->IsIconHandle())
+ {
+ mul->gtOp1 = op1->AsUnOp()->gtGetOp1();
+ op2->AsIntCon()->gtIconVal = -op2->AsIntCon()->gtIconVal;
+ fgUpdateConstTreeValueNumber(op2);
+ DEBUG_DESTROY_NODE(op1);
+
+ op1 = mul->gtOp1;
+ }
+
ssize_t mult = op2->AsIntConCommon()->IconValue();
if (mult == 0)
@@ -13286,41 +13523,19 @@ GenTree* Compiler::fgMorphRetInd(GenTreeUnOp* ret)
if (addr->OperIs(GT_ADDR) && addr->gtGetOp1()->OperIs(GT_LCL_VAR))
{
- // If struct promotion was undone, adjust the annotations
- if (fgGlobalMorph && fgMorphImplicitByRefArgs(addr))
- {
- return ind;
- }
-
// If `return` retypes LCL_VAR as a smaller struct it should not set `doNotEnregister` on that
// LclVar.
// Example: in `Vector128:AsVector2` we have RETURN SIMD8(OBJ SIMD8(ADDR byref(LCL_VAR SIMD16))).
GenTreeLclVar* lclVar = addr->gtGetOp1()->AsLclVar();
+
if (!lvaIsImplicitByRefLocal(lclVar->GetLclNum()))
{
assert(!gtIsActiveCSE_Candidate(addr) && !gtIsActiveCSE_Candidate(ind));
- unsigned indSize;
- if (ind->OperIs(GT_IND))
- {
- indSize = genTypeSize(ind);
- }
- else
- {
- indSize = ind->AsBlk()->GetLayout()->GetSize();
- }
-
- LclVarDsc* varDsc = lvaGetDesc(lclVar);
- unsigned lclVarSize;
- if (!lclVar->TypeIs(TYP_STRUCT))
+ LclVarDsc* varDsc = lvaGetDesc(lclVar);
+ unsigned indSize = ind->Size();
+ unsigned lclVarSize = lvaLclExactSize(lclVar->GetLclNum());
- {
- lclVarSize = genTypeSize(varDsc->TypeGet());
- }
- else
- {
- lclVarSize = varDsc->lvExactSize;
- }
// TODO: change conditions in `canFold` to `indSize <= lclVarSize`, but currently do not support `BITCAST
// int<-SIMD16` etc.
assert((indSize <= lclVarSize) || varDsc->lvDoNotEnregister);
@@ -13355,6 +13570,7 @@ GenTree* Compiler::fgMorphRetInd(GenTreeUnOp* ret)
}
}
}
+
return ind;
}
@@ -14258,23 +14474,6 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac)
}
#endif
- if (fgGlobalMorph)
- {
- // Apply any rewrites for implicit byref arguments before morphing the
- // tree.
-
- if (fgMorphImplicitByRefArgs(tree))
- {
-#ifdef DEBUG
- if (verbose && treesBeforeAfterMorph)
- {
- printf("\nfgMorphTree (%d), after implicit-byref rewrite:\n", thisMorphNum);
- gtDispTree(tree);
- }
-#endif
- }
- }
-
/*-------------------------------------------------------------------------
* fgMorphTree() can potentially replace a tree with another, and the
* caller has to store the return value correctly.
@@ -14924,18 +15123,16 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block)
noway_assert(lastStmt->GetRootNode()->gtOper == GT_SWITCH);
- /* Did we fold the conditional */
+ // Did we fold the conditional
noway_assert(lastStmt->GetRootNode()->AsOp()->gtOp1);
- GenTree* condTree;
- condTree = lastStmt->GetRootNode()->AsOp()->gtOp1;
- GenTree* cond;
- cond = condTree->gtEffectiveVal(true);
+ GenTree* condTree = lastStmt->GetRootNode()->AsOp()->gtOp1;
+ GenTree* cond = condTree->gtEffectiveVal(true);
if (cond->OperIsConst())
{
- /* Yupee - we folded the conditional!
- * Remove the conditional statement */
+ // Yupee - we folded the conditional!
+ // Remove the conditional statement
noway_assert(cond->gtOper == GT_CNS_INT);
@@ -14953,17 +15150,13 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block)
result = FoldResult::FOLD_REMOVED_LAST_STMT;
}
- /* modify the flow graph */
+ // modify the flow graph
- /* Find the actual jump target */
- unsigned switchVal;
- switchVal = (unsigned)cond->AsIntCon()->gtIconVal;
- unsigned jumpCnt;
- jumpCnt = block->bbJumpSwt->bbsCount;
- BasicBlock** jumpTab;
- jumpTab = block->bbJumpSwt->bbsDstTab;
- bool foundVal;
- foundVal = false;
+ // Find the actual jump target
+ size_t switchVal = (size_t)cond->AsIntCon()->gtIconVal;
+ unsigned jumpCnt = block->bbJumpSwt->bbsCount;
+ BasicBlock** jumpTab = block->bbJumpSwt->bbsDstTab;
+ bool foundVal = false;
for (unsigned val = 0; val < jumpCnt; val++, jumpTab++)
{
@@ -14978,20 +15171,20 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block)
{
if (curJump != block->bbNext)
{
- /* transform the basic block into a BBJ_ALWAYS */
+ // transform the basic block into a BBJ_ALWAYS
block->bbJumpKind = BBJ_ALWAYS;
block->bbJumpDest = curJump;
}
else
{
- /* transform the basic block into a BBJ_NONE */
+ // transform the basic block into a BBJ_NONE
block->bbJumpKind = BBJ_NONE;
}
foundVal = true;
}
else
{
- /* Remove 'block' from the predecessor list of 'curJump' */
+ // Remove 'block' from the predecessor list of 'curJump'
fgRemoveRefPred(curJump, block);
}
}
@@ -16702,7 +16895,7 @@ void Compiler::fgMorphLocalField(GenTree* tree, GenTree* parent)
void Compiler::fgResetImplicitByRefRefCount()
{
-#if (defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
#ifdef DEBUG
if (verbose)
{
@@ -16725,7 +16918,7 @@ void Compiler::fgResetImplicitByRefRefCount()
}
}
-#endif // (TARGET_AMD64 && !UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
+#endif // FEATURE_IMPLICIT_BYREFS
}
//------------------------------------------------------------------------
@@ -16734,12 +16927,12 @@ void Compiler::fgResetImplicitByRefRefCount()
// which struct promotions of implicit byrefs to keep or discard.
// For those which are kept, insert the appropriate initialization code.
// For those which are to be discarded, annotate the promoted field locals
-// so that fgMorphImplicitByRefArgs will know to rewrite their appearances
-// using indirections off the pointer parameters.
-
+// so that fgMorphExpandImplicitByRefArg will know to rewrite their
+// appearances using indirections off the pointer parameters.
+//
void Compiler::fgRetypeImplicitByRefArgs()
{
-#if (defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
#ifdef DEBUG
if (verbose)
{
@@ -16860,7 +17053,7 @@ void Compiler::fgRetypeImplicitByRefArgs()
if (undoPromotion)
{
- // Leave lvParentLcl pointing to the parameter so that fgMorphImplicitByRefArgs
+ // Leave lvParentLcl pointing to the parameter so that fgMorphExpandImplicitByRefArg
// will know to rewrite appearances of this local.
assert(fieldVarDsc->lvParentLcl == lclNum);
}
@@ -16897,7 +17090,7 @@ void Compiler::fgRetypeImplicitByRefArgs()
// have these fields.
varDsc->lvFieldCnt = 0;
- // Hijack lvPromoted to communicate to fgMorphImplicitByRefArgs
+ // Hijack lvPromoted to communicate to fgMorphExpandImplicitByRefArg
// whether references to the struct should be rewritten as
// indirections off the pointer (not promoted) or references
// to the new struct local (promoted).
@@ -16909,10 +17102,10 @@ void Compiler::fgRetypeImplicitByRefArgs()
// promotion wanted to promote but that aren't considered profitable to
// rewrite. It hijacks lvFieldLclStart to communicate to
// fgMarkDemotedImplicitByRefArgs that it needs to clean up annotations left
- // on such args for fgMorphImplicitByRefArgs to consult in the interim.
+ // on such args for fgMorphExpandImplicitByRefArg to consult in the interim.
// Here we have an arg that was simply never promoted, so make sure it doesn't
- // have nonzero lvFieldLclStart, since that would confuse fgMorphImplicitByRefArgs
- // and fgMarkDemotedImplicitByRefArgs.
+ // have nonzero lvFieldLclStart, since that would confuse the aforementioned
+ // functions.
assert(varDsc->lvFieldLclStart == 0);
}
@@ -16945,20 +17138,21 @@ void Compiler::fgRetypeImplicitByRefArgs()
}
}
-#endif // (TARGET_AMD64 && !UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
+#endif // FEATURE_IMPLICIT_BYREFS
}
//------------------------------------------------------------------------
// fgMarkDemotedImplicitByRefArgs: Clear annotations for any implicit byrefs that struct promotion
// asked to promote. Appearances of these have now been rewritten
-// (by fgMorphImplicitByRefArgs) using indirections from the pointer
-// parameter or references to the promotion temp, as appropriate.
-
+// (by fgMorphExpandImplicitByRefArg) using indirections from
+// the pointer parameter or references to the promotion temp, as
+// appropriate.
+//
void Compiler::fgMarkDemotedImplicitByRefArgs()
{
JITDUMP("\n*************** In fgMarkDemotedImplicitByRefArgs()\n");
-#if (defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
{
@@ -16970,13 +17164,13 @@ void Compiler::fgMarkDemotedImplicitByRefArgs()
if (varDsc->lvPromoted)
{
- // The parameter is simply a pointer now, so clear lvPromoted. It was left set
- // by fgRetypeImplicitByRefArgs to communicate to fgMorphImplicitByRefArgs that
+ // The parameter is simply a pointer now, so clear lvPromoted. It was left set by
+ // fgRetypeImplicitByRefArgs to communicate to fgMorphExpandImplicitByRefArg that
// appearances of this arg needed to be rewritten to a new promoted struct local.
varDsc->lvPromoted = false;
// Clear the lvFieldLclStart value that was set by fgRetypeImplicitByRefArgs
- // to tell fgMorphImplicitByRefArgs which local is the new promoted struct one.
+ // to tell fgMorphExpandImplicitByRefArg which local is the new promoted struct one.
varDsc->lvFieldLclStart = 0;
}
else if (varDsc->lvFieldLclStart != 0)
@@ -17019,181 +17213,7 @@ void Compiler::fgMarkDemotedImplicitByRefArgs()
}
}
-#endif // (TARGET_AMD64 && !UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
-}
-
-/*****************************************************************************
- *
- * Morph irregular parameters
- * for x64 and ARM64 this means turning them into byrefs, adding extra indirs.
- */
-bool Compiler::fgMorphImplicitByRefArgs(GenTree* tree)
-{
-#if (!defined(TARGET_AMD64) || defined(UNIX_AMD64_ABI)) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
-
- return false;
-
-#else // (TARGET_AMD64 && !UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
-
- bool changed = false;
-
- // Implicit byref morphing needs to know if the reference to the parameter is a
- // child of GT_ADDR or not, so this method looks one level down and does the
- // rewrite whenever a child is a reference to an implicit byref parameter.
- if (tree->gtOper == GT_ADDR)
- {
- if (tree->AsOp()->gtOp1->gtOper == GT_LCL_VAR)
- {
- GenTree* morphedTree = fgMorphImplicitByRefArgs(tree, true);
- changed = (morphedTree != nullptr);
- assert(!changed || (morphedTree == tree));
- }
- }
- else
- {
- for (GenTree** pTree : tree->UseEdges())
- {
- GenTree** pTreeCopy = pTree;
- GenTree* childTree = *pTree;
- if (childTree->gtOper == GT_LCL_VAR)
- {
- GenTree* newChildTree = fgMorphImplicitByRefArgs(childTree, false);
- if (newChildTree != nullptr)
- {
- changed = true;
- *pTreeCopy = newChildTree;
- }
- }
- }
- }
-
- return changed;
-#endif // (TARGET_AMD64 && !UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
-}
-
-GenTree* Compiler::fgMorphImplicitByRefArgs(GenTree* tree, bool isAddr)
-{
- assert((tree->gtOper == GT_LCL_VAR) || ((tree->gtOper == GT_ADDR) && (tree->AsOp()->gtOp1->gtOper == GT_LCL_VAR)));
- assert(isAddr == (tree->gtOper == GT_ADDR));
-
- GenTree* lclVarTree = isAddr ? tree->AsOp()->gtOp1 : tree;
- unsigned lclNum = lclVarTree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* lclVarDsc = lvaGetDesc(lclNum);
-
- CORINFO_FIELD_HANDLE fieldHnd;
- unsigned fieldOffset = 0;
- var_types fieldRefType = TYP_UNKNOWN;
-
- if (lvaIsImplicitByRefLocal(lclNum))
- {
- // The SIMD transformation to coalesce contiguous references to SIMD vector fields will
- // re-invoke the traversal to mark address-taken locals.
- // So, we may encounter a tree that has already been transformed to TYP_BYREF.
- // If we do, leave it as-is.
- if (!varTypeIsStruct(lclVarTree))
- {
- assert(lclVarTree->TypeGet() == TYP_BYREF);
-
- return nullptr;
- }
- else if (lclVarDsc->lvPromoted)
- {
- // fgRetypeImplicitByRefArgs created a new promoted struct local to represent this
- // arg. Rewrite this to refer to the new local.
- assert(lclVarDsc->lvFieldLclStart != 0);
- lclVarTree->AsLclVarCommon()->SetLclNum(lclVarDsc->lvFieldLclStart);
- return tree;
- }
-
- fieldHnd = nullptr;
- }
- else if (lclVarDsc->lvIsStructField && lvaIsImplicitByRefLocal(lclVarDsc->lvParentLcl))
- {
- // This was a field reference to an implicit-by-reference struct parameter that was
- // dependently promoted; update it to a field reference off the pointer.
- // Grab the field handle from the struct field lclVar.
- fieldHnd = lclVarDsc->lvFieldHnd;
- fieldOffset = lclVarDsc->lvFldOffset;
- assert(fieldHnd != nullptr);
- // Update lclNum/lclVarDsc to refer to the parameter
- lclNum = lclVarDsc->lvParentLcl;
- lclVarDsc = lvaGetDesc(lclNum);
- fieldRefType = lclVarTree->TypeGet();
- }
- else
- {
- // We only need to tranform the 'marked' implicit by ref parameters
- return nullptr;
- }
-
- // This is no longer a def of the lclVar, even if it WAS a def of the struct.
- lclVarTree->gtFlags &= ~(GTF_LIVENESS_MASK);
-
- if (isAddr)
- {
- if (fieldHnd == nullptr)
- {
- // change &X into just plain X
- tree->ReplaceWith(lclVarTree, this);
- tree->gtType = TYP_BYREF;
- }
- else
- {
- // change &(X.f) [i.e. GT_ADDR of local for promoted arg field]
- // into &(X, f) [i.e. GT_ADDR of GT_FIELD off ptr param]
- lclVarTree->AsLclVarCommon()->SetLclNum(lclNum);
- lclVarTree->gtType = TYP_BYREF;
- tree->AsOp()->gtOp1 = gtNewFieldRef(fieldRefType, fieldHnd, lclVarTree, fieldOffset);
- }
-
-#ifdef DEBUG
- if (verbose)
- {
- printf("Replacing address of implicit by ref struct parameter with byref:\n");
- }
-#endif // DEBUG
- }
- else
- {
- // Change X into OBJ(X) or FIELD(X, f)
- var_types structType = tree->gtType;
- tree->gtType = TYP_BYREF;
-
- if (fieldHnd)
- {
- tree->AsLclVarCommon()->SetLclNum(lclNum);
- tree = gtNewFieldRef(fieldRefType, fieldHnd, tree, fieldOffset);
- }
- else
- {
- tree = gtNewObjNode(lclVarDsc->GetStructHnd(), tree);
-
- if (structType == TYP_STRUCT)
- {
- gtSetObjGcInfo(tree->AsObj());
- }
- }
-
- // TODO-CQ: If the VM ever stops violating the ABI and passing heap references
- // we could apply TGT_NOT_HEAP here.
- tree->gtFlags = (tree->gtFlags & GTF_COMMON_MASK);
-
-#ifdef DEBUG
- if (verbose)
- {
- printf("Replacing value of implicit by ref struct parameter with indir of parameter:\n");
- }
-#endif // DEBUG
- }
-
-#ifdef DEBUG
- if (verbose)
- {
- gtDispTree(tree);
- }
-#endif // DEBUG
-
- return tree;
+#endif // FEATURE_IMPLICIT_BYREFS
}
//------------------------------------------------------------------------
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index af04a6b11fcc3d..7b6347e9628eec 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -385,13 +385,13 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
bool isSharedConst = false;
int configValue = JitConfig.JitConstCSE();
-#if defined(TARGET_ARM64)
- // ARM64 - allow to combine with nearby offsets, when config is not 2 or 4
- if ((configValue != CONST_CSE_ENABLE_ARM64_NO_SHARING) && (configValue != CONST_CSE_ENABLE_ALL_NO_SHARING))
+#if defined(TARGET_ARMARCH)
+ // ARMARCH - allow to combine with nearby offsets, when config is not 2 or 4
+ if ((configValue != CONST_CSE_ENABLE_ARM_NO_SHARING) && (configValue != CONST_CSE_ENABLE_ALL_NO_SHARING))
{
enableSharedConstCSE = true;
}
-#endif // TARGET_ARM64
+#endif // TARGET_ARMARCH
// All Platforms - also allow to combine with nearby offsets, when config is 3
if (configValue == CONST_CSE_ENABLE_ALL)
@@ -785,10 +785,12 @@ bool Compiler::optValnumCSE_Locate()
}
// Don't allow CSE of constants if it is disabled
- //
if (tree->IsIntegralConst())
{
- if (!enableConstCSE)
+ if (!enableConstCSE &&
+ // Unconditionally allow these constant handles to be CSE'd
+ !tree->IsIconHandle(GTF_ICON_STATIC_HDL) && !tree->IsIconHandle(GTF_ICON_CLASS_HDL) &&
+ !tree->IsIconHandle(GTF_ICON_STR_HDL))
{
continue;
}
@@ -2960,9 +2962,9 @@ class CSE_Heuristic
do
{
/* Process the next node in the list */
- GenTree* exp = lst->tslTree;
- Statement* stmt = lst->tslStmt;
- BasicBlock* blk = lst->tslBlock;
+ GenTree* const exp = lst->tslTree;
+ Statement* const stmt = lst->tslStmt;
+ BasicBlock* const blk = lst->tslBlock;
/* Advance to the next node in the list */
lst = lst->tslNext;
@@ -3210,9 +3212,9 @@ class CSE_Heuristic
noway_assert(asg->AsOp()->gtOp2 == val);
}
- // assign the proper Value Numbers
- asg->gtVNPair.SetBoth(ValueNumStore::VNForVoid()); // The GT_ASG node itself is $VN.Void
- asg->AsOp()->gtOp1->gtVNPair = val->gtVNPair; // The dest op is the same as 'val'
+ // Assign the proper Value Numbers.
+ asg->gtVNPair = ValueNumStore::VNPForVoid(); // The GT_ASG node itself is $VN.Void.
+ asg->AsOp()->gtOp1->gtVNPair = ValueNumStore::VNPForVoid(); // As is the LHS.
noway_assert(asg->AsOp()->gtOp1->gtOper == GT_LCL_VAR);
@@ -3261,7 +3263,7 @@ class CSE_Heuristic
cseUse->SetDoNotCSE();
}
}
- cseUse->gtVNPair = val->gtVNPair; // The 'cseUse' is equal to 'val'
+ cseUse->gtVNPair = exp->gtVNPair; // The 'cseUse' is equal to the original expression.
/* Create a comma node for the CSE assignment */
cse = m_pCompiler->gtNewOperNode(GT_COMMA, expTyp, origAsg, cseUse);
@@ -3582,7 +3584,6 @@ bool Compiler::optIsCSEcandidate(GenTree* tree)
case GT_ARR_ELEM:
case GT_ARR_LENGTH:
- case GT_LCL_FLD:
return true;
case GT_LCL_VAR:
@@ -3685,7 +3686,9 @@ bool Compiler::optIsCSEcandidate(GenTree* tree)
return true; // allow Intrinsics to be CSE-ed
case GT_OBJ:
- return varTypeIsEnregisterable(type); // Allow enregisterable GT_OBJ's to be CSE-ed. (i.e. SIMD types)
+ case GT_LCL_FLD:
+ // TODO-1stClassStructs: support CSE for enregisterable TYP_STRUCTs.
+ return varTypeIsEnregisterable(type);
case GT_COMMA:
return true; // Allow GT_COMMA nodes to be CSE-ed.
diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp
index b848d6400bca88..315b19caf7ddf8 100644
--- a/src/coreclr/jit/optimizer.cpp
+++ b/src/coreclr/jit/optimizer.cpp
@@ -1725,9 +1725,13 @@ class LoopSearch
// Thus, we have to be very careful and after entry discovery check that it is indeed
// the only place we enter the loop (especially for non-reducible flow graphs).
+ JITDUMP("FindLoop: checking head:" FMT_BB " top:" FMT_BB " bottom:" FMT_BB "\n", head->bbNum, top->bbNum,
+ bottom->bbNum);
+
if (top->bbNum > bottom->bbNum) // is this a backward edge? (from BOTTOM to TOP)
{
// Edge from BOTTOM to TOP is not a backward edge
+ JITDUMP(" " FMT_BB "->" FMT_BB " is not a backedge\n", bottom->bbNum, top->bbNum);
return false;
}
@@ -1735,11 +1739,13 @@ class LoopSearch
{
// Not a true back-edge; bottom is a block added to reconnect fall-through during
// loop processing, so its block number does not reflect its position.
+ JITDUMP(" " FMT_BB "->" FMT_BB " is not a true backedge\n", bottom->bbNum, top->bbNum);
return false;
}
if (bottom->KindIs(BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, BBJ_CALLFINALLY, BBJ_SWITCH))
{
+ JITDUMP(" bottom odd jump kind\n");
// BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop.
// BBJ_SWITCH that has a backward jump appears only for labeled break.
return false;
@@ -1759,6 +1765,7 @@ class LoopSearch
if (entry == nullptr)
{
// For now, we only recognize loops where HEAD has some successor ENTRY in the loop.
+ JITDUMP(" can't find entry\n");
return false;
}
@@ -1773,12 +1780,14 @@ class LoopSearch
if (!HasSingleEntryCycle())
{
// There isn't actually a loop between TOP and BOTTOM
+ JITDUMP(" not single entry cycle\n");
return false;
}
if (!loopBlocks.IsMember(top->bbNum))
{
// The "back-edge" we identified isn't actually part of the flow cycle containing ENTRY
+ JITDUMP(" top not in loop\n");
return false;
}
@@ -1828,6 +1837,7 @@ class LoopSearch
if (!MakeCompactAndFindExits())
{
// Unable to preserve well-formed loop during compaction.
+ JITDUMP(" can't compact\n");
return false;
}
@@ -1928,6 +1938,7 @@ class LoopSearch
}
else if (!comp->fgDominate(entry, block))
{
+ JITDUMP(" (find cycle) entry:" FMT_BB " does not dominate " FMT_BB "\n", entry->bbNum, block->bbNum);
return false;
}
@@ -1960,6 +1971,8 @@ class LoopSearch
}
// There are multiple entries to this loop, don't consider it.
+
+ JITDUMP(" (find cycle) multiple entry:" FMT_BB "\n", block->bbNum);
return false;
}
@@ -1967,6 +1980,7 @@ class LoopSearch
if (predBlock == entry)
{
// We have indeed found a cycle in the flow graph.
+ JITDUMP(" (find cycle) found cycle\n");
isFirstVisit = !foundCycle;
foundCycle = true;
assert(loopBlocks.IsMember(predBlock->bbNum));
@@ -2664,6 +2678,9 @@ void Compiler::optFindNaturalLoops()
// Make sure that loops are canonical: that every loop has a unique "top", by creating an empty "nop"
// one, if necessary, for loops containing others that share a "top."
+ //
+ // Also make sure that no loop's "bottom" is another loop's "head".
+ //
for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
{
// Traverse the outermost loops as entries into the loop nest; so skip non-outermost.
@@ -2874,49 +2891,229 @@ bool Compiler::optIsLoopEntry(BasicBlock* block) const
return false;
}
-// Canonicalize the loop nest rooted at parent loop 'loopInd'.
-// Returns 'true' if the flow graph is modified.
+//-----------------------------------------------------------------------------
+// optCanonicalizeLoopNest: Canonicalize a loop nest
+//
+// Arguments:
+// loopInd - index of outermost loop in the nest
+//
+// Returns:
+// true if the flow graph was modified
+//
+// Notes:
+// For loopInd and all contained loops, ensures each loop top's back edges
+// only come from this loop.
+//
+// Will split top blocks and redirect edges if needed.
+//
bool Compiler::optCanonicalizeLoopNest(unsigned char loopInd)
{
- bool modified = false;
-
- // Is the top of the current loop in any nested loop?
- if (optLoopTable[loopInd].lpTop->bbNatLoopNum != loopInd)
- {
- if (optCanonicalizeLoop(loopInd))
- {
- modified = true;
- }
- }
+ // First canonicalize the loop.
+ //
+ bool modified = optCanonicalizeLoop(loopInd);
+ // Then any children.
+ //
for (unsigned char child = optLoopTable[loopInd].lpChild; //
child != BasicBlock::NOT_IN_LOOP; //
child = optLoopTable[child].lpSibling)
{
- if (optCanonicalizeLoopNest(child))
- {
- modified = true;
- }
+ modified |= optCanonicalizeLoopNest(child);
}
return modified;
}
+//-----------------------------------------------------------------------------
+// optCanonicalizeLoop: ensure that each loop top's back edges come only from
+// blocks in the same loop, and that no loop head/bottom blocks coincide.
+//
+// Arguments:
+// loopInd - index of the loop to consider
+//
+// Returns:
+// true if flow changes were made
+//
+// Notes:
+//
+// Back edges incident on loop top fall into one three groups:
+//
+// (1) Outer non-loop backedges (preds dominated by entry where pred is not in loop)
+// (2) The canonical backedge (pred == bottom)
+// (3) Nested loop backedges or nested non-loop backedges
+// (preds dominated by entry, where pred is in loop, pred != bottom)
+//
+// We assume dominance has already been established by loop recognition (that is,
+// anything classified as a loop will have all backedges dominated by loop entry,
+// so the only possible non-backedge predecessor of top will be head).
+//
+// We cannot check dominance here as the flow graph is being modified.
+//
+// If either set (1) or (3) is non-empty the loop is not canonical.
+//
+// This method will split the loop top into two or three blocks depending on
+// whether (1) or (3) is non-empty, and redirect the edges accordingly.
+//
+// Loops are canoncalized outer to inner, so inner loops should never see outer loop
+// non-backedges, as the parent loop canonicalization should have handled them.
+//
bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
{
- // Is the top uniquely part of the current loop?
- BasicBlock* t = optLoopTable[loopInd].lpTop;
+ bool modified = false;
+ BasicBlock* const b = optLoopTable[loopInd].lpBottom;
+ BasicBlock* const t = optLoopTable[loopInd].lpTop;
+ BasicBlock* const h = optLoopTable[loopInd].lpHead;
+ BasicBlock* const e = optLoopTable[loopInd].lpEntry;
+
+ // Look for case (1)
+ //
+ bool doOuterCanon = false;
+
+ for (BasicBlock* const topPredBlock : t->PredBlocks())
+ {
+ const bool predIsInLoop = (t->bbNum <= topPredBlock->bbNum) && (topPredBlock->bbNum <= b->bbNum);
+ if (predIsInLoop || (topPredBlock == h))
+ {
+ // no action needed
+ }
+ else
+ {
+ JITDUMP("in optCanonicalizeLoop: " FMT_LP " top " FMT_BB " (entry " FMT_BB " bottom " FMT_BB
+ ") %shas a non-loop backedge from " FMT_BB "%s\n",
+ loopInd, t->bbNum, e->bbNum, b->bbNum, doOuterCanon ? "also " : "", topPredBlock->bbNum,
+ doOuterCanon ? "" : ": need to canonicalize non-loop backedges");
+ doOuterCanon = true;
+ }
+ }
- if (t->bbNatLoopNum == loopInd)
+ if (doOuterCanon)
{
+ const bool didCanon = optCanonicalizeLoopCore(loopInd, LoopCanonicalizationOption::Outer);
+ assert(didCanon);
+ modified |= didCanon;
+ }
+
+ // Look for case (3)
+ //
+ // Outer canon should not update loop top.
+ //
+ assert(t == optLoopTable[loopInd].lpTop);
+ if (t->bbNatLoopNum != loopInd)
+ {
+ JITDUMP("in optCanonicalizeLoop: " FMT_LP " has top " FMT_BB " (entry " FMT_BB " bottom " FMT_BB
+ ") with natural loop number " FMT_LP ": need to canonicalize nested inner loop backedges\n",
+ loopInd, t->bbNum, e->bbNum, b->bbNum, t->bbNatLoopNum);
+
+ const bool didCanon = optCanonicalizeLoopCore(loopInd, LoopCanonicalizationOption::Current);
+ assert(didCanon);
+ modified |= didCanon;
+ }
+
+ // Check if this loopInd head is also the bottom of some sibling.
+ // If so, add a block in between to serve as the new head.
+ //
+ auto repairLoop = [this](unsigned char loopInd, unsigned char sibling) {
+
+ BasicBlock* const h = optLoopTable[loopInd].lpHead;
+ BasicBlock* const siblingB = optLoopTable[sibling].lpBottom;
+
+ if (h == siblingB)
+ {
+ // We have
+ //
+ // sibling.B (== loopInd.H) -e-> loopInd.T
+ //
+ // where e is a "critical edge", that is
+ // * sibling.B has other successors (notably sibling.T),
+ // * loopInd.T has other predecessors (notably loopInd.B)
+ //
+ // turn this into
+ //
+ // sibling.B -> newH (== loopInd.H) -> loopInd.T
+ //
+ // Ideally we'd just call fgSplitEdge, but we are
+ // not keeping pred lists in good shape.
+ //
+ BasicBlock* const t = optLoopTable[loopInd].lpTop;
+ assert(siblingB->bbJumpKind == BBJ_COND);
+ assert(siblingB->bbNext == t);
+
+ JITDUMP(FMT_LP " head " FMT_BB " is also " FMT_LP " bottom\n", loopInd, h->bbNum, sibling);
+
+ BasicBlock* const newH = fgNewBBbefore(BBJ_NONE, t, /*extendRegion*/ true);
+
+ // Anything that flows into sibling will flow here.
+ // So we use sibling.H as our best guess for weight.
+ //
+ newH->inheritWeight(optLoopTable[sibling].lpHead);
+ newH->bbNatLoopNum = optLoopTable[loopInd].lpParent;
+ optUpdateLoopHead(loopInd, h, newH);
+
+ return true;
+ }
return false;
+ };
+
+ if (optLoopTable[loopInd].lpParent == BasicBlock::NOT_IN_LOOP)
+ {
+ // check against all other top-level loops
+ //
+ for (unsigned char sibling = 0; sibling < optLoopCount; sibling++)
+ {
+ if (optLoopTable[sibling].lpParent != BasicBlock::NOT_IN_LOOP)
+ {
+ continue;
+ }
+
+ modified |= repairLoop(loopInd, sibling);
+ }
+ }
+ else
+ {
+ // check against all other sibling loops
+ //
+ const unsigned char parentLoop = optLoopTable[loopInd].lpParent;
+
+ for (unsigned char sibling = optLoopTable[parentLoop].lpChild; //
+ sibling != BasicBlock::NOT_IN_LOOP; //
+ sibling = optLoopTable[sibling].lpSibling)
+ {
+ if (sibling == loopInd)
+ {
+ continue;
+ }
+
+ modified |= repairLoop(loopInd, sibling);
+ }
+ }
+
+ if (modified)
+ {
+ JITDUMP("Done canonicalizing " FMT_LP "\n\n", loopInd);
}
- JITDUMP("in optCanonicalizeLoop: " FMT_LP " has top " FMT_BB " (bottom " FMT_BB ") with natural loop number " FMT_LP
- ": need to canonicalize\n",
- loopInd, t->bbNum, optLoopTable[loopInd].lpBottom->bbNum, t->bbNatLoopNum);
+ return modified;
+}
- // Otherwise, the top of this loop is also part of a nested loop.
+//-----------------------------------------------------------------------------
+// optCanonicalizeLoopCore: ensure that each loop top's back edges come do not
+// come from outer/inner loops.
+//
+// Arguments:
+// loopInd - index of the loop to consider
+// option - which set of edges to move when canonicalizing
+//
+// Returns:
+// true if flow changes were made
+//
+// Notes:
+// option ::Outer retargets all backedges that do not come from loops in the block.
+// option ::Current retargets the canonical backedge (from bottom)
+//
+bool Compiler::optCanonicalizeLoopCore(unsigned char loopInd, LoopCanonicalizationOption option)
+{
+ // Otherwise, the top of this loop is also part of a nested loop or has
+ // non-loop backedges.
//
// Insert a new unique top for this loop. We must be careful to put this new
// block in the correct EH region. Note that t->bbPrev might be in a different
@@ -2992,17 +3189,41 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
// want to copy the EH region of the back edge, since that would create a block
// outside of and disjoint with the "try" region of the back edge. However, to
// simplify things, we disqualify this type of loop, so we should never see this here.
-
- BasicBlock* h = optLoopTable[loopInd].lpHead;
- BasicBlock* b = optLoopTable[loopInd].lpBottom;
+ //
+ BasicBlock* const b = optLoopTable[loopInd].lpBottom;
+ BasicBlock* const t = optLoopTable[loopInd].lpTop;
+ BasicBlock* const h = optLoopTable[loopInd].lpHead;
// The loop must be entirely contained within a single handler region.
assert(BasicBlock::sameHndRegion(t, b));
+ // We expect h to be already "canonical" -- that is, it falls through to t
+ // and is not a degenerate BBJ_COND (both branches and falls through to t)
+ // or a side entry to the loop.
+ //
+ // Because of this, introducing a block before t automatically gives us
+ // the right flow out of h.
+ //
+ assert(h->bbNext == t);
+ assert(h->bbFallsThrough());
+ assert((h->bbJumpKind == BBJ_NONE) || (h->bbJumpKind == BBJ_COND));
+ if (h->bbJumpKind == BBJ_COND)
+ {
+ BasicBlock* const hj = h->bbJumpDest;
+ assert((hj->bbNum < t->bbNum) || (hj->bbNum > b->bbNum));
+ }
+
// If the bottom block is in the same "try" region, then we extend the EH
// region. Otherwise, we add the new block outside the "try" region.
- const bool extendRegion = BasicBlock::sameTryRegion(t, b);
- BasicBlock* newT = fgNewBBbefore(BBJ_NONE, t, extendRegion);
+ //
+ const bool extendRegion = BasicBlock::sameTryRegion(t, b);
+ BasicBlock* const newT = fgNewBBbefore(BBJ_NONE, t, extendRegion);
+
+ // Initially give newT the same weight as t; we will subtract from
+ // this for each edge that does not move from t to newT.
+ //
+ newT->inheritWeight(t);
+
if (!extendRegion)
{
// We need to set the EH region manually. Set it to be the same
@@ -3010,112 +3231,154 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
newT->copyEHRegion(b);
}
+ // NewT will be the target for the outer/current loop's backedge(s).
+ //
+ BlockToBlockMap* const blockMap = new (getAllocator(CMK_LoopOpt)) BlockToBlockMap(getAllocator(CMK_LoopOpt));
+ blockMap->Set(t, newT);
+
// The new block can reach the same set of blocks as the old one, but don't try to reflect
// that in its reachability set here -- creating the new block may have changed the BlockSet
// representation from short to long, and canonicalizing loops is immediately followed by
// a call to fgUpdateChangedFlowGraph which will recompute the reachability sets anyway.
- // Redirect the "bottom" of the current loop to "newT".
- BlockToBlockMap* blockMap = new (getAllocator(CMK_LoopOpt)) BlockToBlockMap(getAllocator(CMK_LoopOpt));
- blockMap->Set(t, newT);
- optRedirectBlock(b, blockMap);
-
- // Redirect non-loop preds of "t" to also go to "newT". Inner loops that also branch to "t" should continue
- // to do so. However, there maybe be other predecessors from outside the loop nest that need to be updated
- // to point to "newT". This normally wouldn't happen, since they too would be part of the loop nest. However,
- // they might have been prevented from participating in the loop nest due to different EH nesting, or some
- // other reason.
- //
- // Note that optRedirectBlock doesn't update the predecessors list. So, if the same 't' block is processed
- // multiple times while canonicalizing multiple loop nests, we'll attempt to redirect a predecessor multiple times.
- // This is ok, because after the first redirection, the topPredBlock branch target will no longer match the source
- // edge of the blockMap, so nothing will happen.
bool firstPred = true;
for (BasicBlock* const topPredBlock : t->PredBlocks())
{
- // Skip if topPredBlock is in the loop.
- // Note that this uses block number to detect membership in the loop. We are adding blocks during
- // canonicalization, and those block numbers will be new, and larger than previous blocks. However, we work
- // outside-in, so we shouldn't encounter the new blocks at the loop boundaries, or in the predecessor lists.
- if (t->bbNum <= topPredBlock->bbNum && topPredBlock->bbNum <= b->bbNum)
+ // We set profile weight of newT assuming all edges would
+ // be redirected there. So, if we don't redirect this edge,
+ // this is how much we'll have to adjust newT's weight.
+ //
+ weight_t weightAdjust = BB_ZERO_WEIGHT;
+
+ if (option == LoopCanonicalizationOption::Current)
{
- JITDUMP("in optCanonicalizeLoop: 'top' predecessor " FMT_BB " is in the range of " FMT_LP " (" FMT_BB
- ".." FMT_BB "); not redirecting its bottom edge\n",
- topPredBlock->bbNum, loopInd, t->bbNum, b->bbNum);
- continue;
+ // Redirect the (one and only) true backedge of this loop.
+ //
+ if (topPredBlock != b)
+ {
+ if ((topPredBlock != h) && topPredBlock->hasProfileWeight())
+ {
+ // Note this may overstate the adjustment, if topPredBlock is BBJ_COND.
+ //
+ weightAdjust = topPredBlock->bbWeight;
+ }
+ }
+ else
+ {
+ JITDUMP("in optCanonicalizeLoop (current): redirect bottom->top backedge " FMT_BB " -> " FMT_BB
+ " to " FMT_BB " -> " FMT_BB "\n",
+ topPredBlock->bbNum, t->bbNum, topPredBlock->bbNum, newT->bbNum);
+ optRedirectBlock(b, blockMap);
+ }
}
-
- JITDUMP("in optCanonicalizeLoop: redirect top predecessor " FMT_BB " to " FMT_BB "\n", topPredBlock->bbNum,
- newT->bbNum);
- optRedirectBlock(topPredBlock, blockMap);
-
- // When we have profile data then the 'newT' block will inherit topPredBlock profile weight
- if (topPredBlock->hasProfileWeight())
+ else if (option == LoopCanonicalizationOption::Outer)
{
- // This corrects an issue when the topPredBlock has a profile based weight
+ // Redirect non-loop preds of "t" to go to "newT". Inner loops that also branch to "t" should continue
+ // to do so. However, there maybe be other predecessors from outside the loop nest that need to be updated
+ // to point to "newT". This normally wouldn't happen, since they too would be part of the loop nest.
+ // However,
+ // they might have been prevented from participating in the loop nest due to different EH nesting, or some
+ // other reason.
+ //
+ // Skip if topPredBlock is in the loop.
+ // Note that this uses block number to detect membership in the loop. We are adding blocks during
+ // canonicalization, and those block numbers will be new, and larger than previous blocks. However, we work
+ // outside-in, so we shouldn't encounter the new blocks at the loop boundaries, or in the predecessor lists.
//
- if (firstPred)
+ if ((t->bbNum <= topPredBlock->bbNum) && (topPredBlock->bbNum <= b->bbNum))
{
- JITDUMP("in optCanonicalizeLoop: block " FMT_BB " will inheritWeight from " FMT_BB "\n", newT->bbNum,
- topPredBlock->bbNum);
-
- newT->inheritWeight(topPredBlock);
- firstPred = false;
+ if (topPredBlock->hasProfileWeight())
+ {
+ // Note this may overstate the adjustment, if topPredBlock is BBJ_COND.
+ //
+ weightAdjust = topPredBlock->bbWeight;
+ }
}
else
{
- JITDUMP("in optCanonicalizeLoop: block " FMT_BB " will also contribute to the weight of " FMT_BB "\n",
- newT->bbNum, topPredBlock->bbNum);
+ JITDUMP("in optCanonicalizeLoop (outer): redirect %s->top %sedge " FMT_BB " -> " FMT_BB " to " FMT_BB
+ " -> " FMT_BB "\n",
+ topPredBlock == h ? "head" : "nonloop", topPredBlock == h ? "" : "back", topPredBlock->bbNum,
+ t->bbNum, topPredBlock->bbNum, newT->bbNum);
+ optRedirectBlock(topPredBlock, blockMap);
+ }
+ }
+ else
+ {
+ unreached();
+ }
+
+ if (weightAdjust > BB_ZERO_WEIGHT)
+ {
+ JITDUMP("in optCanonicalizeLoop: removing block " FMT_BB " weight " FMT_WT " from " FMT_BB "\n",
+ topPredBlock->bbNum, weightAdjust, newT->bbNum);
- weight_t newWeight = newT->getBBWeight(this) + topPredBlock->getBBWeight(this);
- newT->setBBProfileWeight(newWeight);
+ if (newT->bbWeight >= weightAdjust)
+ {
+ newT->setBBProfileWeight(newT->bbWeight - weightAdjust);
+ }
+ else if (newT->bbWeight > BB_ZERO_WEIGHT)
+ {
+ newT->setBBProfileWeight(BB_ZERO_WEIGHT);
}
}
}
+ assert(h->bbNext == newT);
assert(newT->bbNext == t);
- // If it had been a do-while loop (top == entry), update entry, as well.
- BasicBlock* origE = optLoopTable[loopInd].lpEntry;
- if (optLoopTable[loopInd].lpTop == origE)
+ // With the Option::Current we are changing which block is loop top.
+ // Make suitable updates.
+ //
+ if (option == LoopCanonicalizationOption::Current)
{
- optLoopTable[loopInd].lpEntry = newT;
- }
- optLoopTable[loopInd].lpTop = newT;
+ JITDUMP("in optCanonicalizeLoop (current): " FMT_BB " is now the top of loop " FMT_LP "\n", newT->bbNum,
+ loopInd);
+
+ optLoopTable[loopInd].lpTop = newT;
+ newT->bbNatLoopNum = loopInd;
- newT->bbNatLoopNum = loopInd;
+ // If loopInd was a do-while loop (top == entry), update entry, as well.
+ //
+ BasicBlock* const origE = optLoopTable[loopInd].lpEntry;
+ if (origE == t)
+ {
+ JITDUMP("updating entry of " FMT_LP " to " FMT_BB "\n", loopInd, newT->bbNum);
+ optLoopTable[loopInd].lpEntry = newT;
+ }
- JITDUMP("in optCanonicalizeLoop: made new block " FMT_BB " [%p] the new unique top of loop %d.\n", newT->bbNum,
- dspPtr(newT), loopInd);
+ // If any loops nested in "loopInd" have the same head and entry as "loopInd",
+ // it must be the case that they were do-while's (since "h" fell through to the entry).
+ // The new node "newT" becomes the head of such loops.
+ for (unsigned char childLoop = optLoopTable[loopInd].lpChild; //
+ childLoop != BasicBlock::NOT_IN_LOOP; //
+ childLoop = optLoopTable[childLoop].lpSibling)
+ {
+ if ((optLoopTable[childLoop].lpEntry == origE) && (optLoopTable[childLoop].lpHead == h) &&
+ (newT->bbJumpKind == BBJ_NONE) && (newT->bbNext == origE))
+ {
+ optUpdateLoopHead(childLoop, h, newT);
- // Make sure the head block still goes to the entry...
- if (h->bbJumpKind == BBJ_NONE && h->bbNext != optLoopTable[loopInd].lpEntry)
- {
- h->bbJumpKind = BBJ_ALWAYS;
- h->bbJumpDest = optLoopTable[loopInd].lpEntry;
+ // Fix pred list here, so when we walk preds of child loop tops
+ // we see the right blocks.
+ //
+ fgReplacePred(optLoopTable[childLoop].lpTop, h, newT);
+ }
+ }
}
- else if (h->bbJumpKind == BBJ_COND && h->bbNext == newT && newT != optLoopTable[loopInd].lpEntry)
+ else if (option == LoopCanonicalizationOption::Outer)
{
- BasicBlock* h2 = fgNewBBafter(BBJ_ALWAYS, h, /*extendRegion*/ true);
- optLoopTable[loopInd].lpHead = h2;
- h2->bbJumpDest = optLoopTable[loopInd].lpEntry;
- h2->bbStmtList = nullptr;
- fgInsertStmtAtEnd(h2, fgNewStmtFromTree(gtNewOperNode(GT_NOP, TYP_VOID, nullptr)));
- }
+ JITDUMP("in optCanonicalizeLoop (outer): " FMT_BB " is outside of loop " FMT_LP "\n", newT->bbNum, loopInd);
- // If any loops nested in "loopInd" have the same head and entry as "loopInd",
- // it must be the case that they were do-while's (since "h" fell through to the entry).
- // The new node "newT" becomes the head of such loops.
- for (unsigned char childLoop = optLoopTable[loopInd].lpChild; //
- childLoop != BasicBlock::NOT_IN_LOOP; //
- childLoop = optLoopTable[childLoop].lpSibling)
- {
- if (optLoopTable[childLoop].lpEntry == origE && optLoopTable[childLoop].lpHead == h &&
- newT->bbJumpKind == BBJ_NONE && newT->bbNext == origE)
- {
- optUpdateLoopHead(childLoop, h, newT);
- }
+ // If we are lifting outer backeges, then newT belongs to our parent loop
+ //
+ newT->bbNatLoopNum = optLoopTable[loopInd].lpParent;
+
+ // newT is now the header of this loop
+ //
+ optUpdateLoopHead(loopInd, h, newT);
}
+
return true;
}
@@ -3151,6 +3414,22 @@ bool Compiler::optLoopContains(unsigned l1, unsigned l2) const
}
}
+//-----------------------------------------------------------------------------
+// optLoopEntry: For a given preheader of a loop, returns the lpEntry.
+//
+// Arguments:
+// preHeader -- preheader of a loop
+//
+// Returns:
+// Corresponding loop entry block.
+//
+BasicBlock* Compiler::optLoopEntry(BasicBlock* preHeader)
+{
+ assert((preHeader->bbFlags & BBF_LOOP_PREHEADER) != 0);
+
+ return (preHeader->bbJumpDest == nullptr) ? preHeader->bbNext : preHeader->bbJumpDest;
+}
+
//-----------------------------------------------------------------------------
// optUpdateLoopHead: Replace the `head` block of a loop in the loop table.
// Considers all child loops that might share the same head (recursively).
@@ -5992,6 +6271,7 @@ void Compiler::optPerformHoistExpr(GenTree* origExpr, BasicBlock* exprBb, unsign
{
printf("\nHoisting a copy of ");
printTreeID(origExpr);
+ printf(" " FMT_VN, origExpr->gtVNPair.GetLiberal());
printf(" from " FMT_BB " into PreHeader " FMT_BB " for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n",
exprBb->bbNum, optLoopTable[lnum].lpHead->bbNum, lnum, optLoopTable[lnum].lpTop->bbNum,
optLoopTable[lnum].lpBottom->bbNum);
@@ -6262,46 +6542,51 @@ void Compiler::optHoistLoopNest(unsigned lnum, LoopHoistContext* hoistCtxt)
m_loopsConsidered++;
#endif // LOOP_HOIST_STATS
- optHoistThisLoop(lnum, hoistCtxt);
-
- VNSet* hoistedInCurLoop = hoistCtxt->ExtractHoistedInCurLoop();
+ BasicBlockList* preHeadersOfChildLoops = nullptr;
+ BasicBlockList* firstPreHeader = nullptr;
if (optLoopTable[lnum].lpChild != BasicBlock::NOT_IN_LOOP)
{
- // Add the ones hoisted in "lnum" to "hoistedInParents" for any nested loops.
- // TODO-Cleanup: we should have a set abstraction for loops.
- if (hoistedInCurLoop != nullptr)
- {
- for (VNSet::KeyIterator keys = hoistedInCurLoop->Begin(); !keys.Equal(hoistedInCurLoop->End()); ++keys)
- {
-#ifdef DEBUG
- bool b;
- assert(!hoistCtxt->m_hoistedInParentLoops.Lookup(keys.Get(), &b));
-#endif
- hoistCtxt->m_hoistedInParentLoops.Set(keys.Get(), true);
- }
- }
+ BitVecTraits m_visitedTraits(fgBBNumMax * 2, this);
+ BitVec m_visited(BitVecOps::MakeEmpty(&m_visitedTraits));
for (unsigned child = optLoopTable[lnum].lpChild; child != BasicBlock::NOT_IN_LOOP;
child = optLoopTable[child].lpSibling)
{
optHoistLoopNest(child, hoistCtxt);
- }
- // Now remove them.
- // TODO-Cleanup: we should have a set abstraction for loops.
- if (hoistedInCurLoop != nullptr)
- {
- for (VNSet::KeyIterator keys = hoistedInCurLoop->Begin(); !keys.Equal(hoistedInCurLoop->End()); ++keys)
+ if (optLoopTable[child].lpFlags & LPFLG_HAS_PREHEAD)
{
- // Note that we asserted when we added these that they hadn't been members, so removing is appropriate.
- hoistCtxt->m_hoistedInParentLoops.Remove(keys.Get());
+ // If any preheaders were found, add them to the tracking list
+
+ BasicBlock* preHeaderBlock = optLoopTable[child].lpHead;
+ if (!BitVecOps::IsMember(&m_visitedTraits, m_visited, preHeaderBlock->bbNum))
+ {
+ BitVecOps::AddElemD(&m_visitedTraits, m_visited, preHeaderBlock->bbNum);
+ JITDUMP(" PREHEADER: " FMT_BB "\n", preHeaderBlock->bbNum);
+
+ // Here, we are arranging the blocks in reverse execution order, so when they are pushed
+ // on the stack that hoist these blocks further sees them in execution order.
+ if (firstPreHeader == nullptr)
+ {
+ preHeadersOfChildLoops = new (this, CMK_LoopHoist) BasicBlockList(preHeaderBlock, nullptr);
+ firstPreHeader = preHeadersOfChildLoops;
+ }
+ else
+ {
+ preHeadersOfChildLoops->next =
+ new (this, CMK_LoopHoist) BasicBlockList(preHeaderBlock, nullptr);
+ preHeadersOfChildLoops = preHeadersOfChildLoops->next;
+ }
+ }
}
}
}
+
+ optHoistThisLoop(lnum, hoistCtxt, firstPreHeader);
}
-void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt)
+void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt, BasicBlockList* existingPreHeaders)
{
LoopDsc* pLoopDsc = &optLoopTable[lnum];
@@ -6413,24 +6698,61 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt)
// or side-effect dependent things.
//
// We really should consider hoisting from conditionally executed blocks, if they are frequently executed
- // and it is safe to evaluate the tree early.
- //
- // In particular if we have a loop nest, when scanning the outer loop we should consider hoisting from blocks
- // in enclosed loops. However, this is likely to scale poorly, and we really should instead start
- // hoisting inner to outer.
+ // and it is safe to evaluate the tree early
//
ArrayStack defExec(getAllocatorLoopHoist());
+
+ bool pushAllPreheaders = false;
+
if (pLoopDsc->lpExitCnt == 1)
{
assert(pLoopDsc->lpExit != nullptr);
- JITDUMP(" Only considering hoisting in blocks that dominate exit block " FMT_BB "\n", pLoopDsc->lpExit->bbNum);
- BasicBlock* cur = pLoopDsc->lpExit;
+ JITDUMP(" Considering hoisting in blocks that either dominate exit block " FMT_BB
+ " or preheaders of nested loops, if any:\n",
+ pLoopDsc->lpExit->bbNum);
+
// Push dominators, until we reach "entry" or exit the loop.
+ // Also push the preheaders that were added for the nested loops,
+ // if any, along the way such that in the final list, the dominating
+ // blocks are visited before the dominated blocks.
+ //
+ // TODO-CQ: In future, we should create preheaders upfront before building
+ // dominators so we don't have to do this extra work here.
+ BasicBlock* cur = pLoopDsc->lpExit;
+ BasicBlockList* preHeadersList = existingPreHeaders;
+
while (cur != nullptr && pLoopDsc->lpContains(cur) && cur != pLoopDsc->lpEntry)
{
+ JITDUMP(" -- " FMT_BB " (dominate exit block)\n", cur->bbNum);
defExec.Push(cur);
cur = cur->bbIDom;
+
+ if (preHeadersList != nullptr)
+ {
+ BasicBlock* preHeaderBlock = preHeadersList->block;
+ BasicBlock* lpEntry = optLoopEntry(preHeaderBlock);
+ if (cur->bbNum < lpEntry->bbNum)
+ {
+ JITDUMP(" -- " FMT_BB " (preheader of " FMT_LP ")\n", preHeaderBlock->bbNum,
+ lpEntry->bbNatLoopNum);
+
+ defExec.Push(preHeaderBlock);
+ preHeadersList = preHeadersList->next;
+ }
+ }
+ }
+
+ // Push the remaining preheaders, if any. This usually will happen if entry
+ // and exit blocks of lnum is same.
+ while (preHeadersList != nullptr)
+ {
+ BasicBlock* preHeaderBlock = preHeadersList->block;
+ JITDUMP(" -- " FMT_BB " (preheader of " FMT_LP ")\n", preHeaderBlock->bbNum,
+ optLoopEntry(preHeaderBlock)->bbNatLoopNum);
+ defExec.Push(preHeaderBlock);
+ preHeadersList = preHeadersList->next;
}
+
// If we didn't reach the entry block, give up and *just* push the entry block.
if (cur != pLoopDsc->lpEntry)
{
@@ -6438,17 +6760,37 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt)
"block " FMT_BB "\n",
pLoopDsc->lpEntry->bbNum);
defExec.Reset();
+ pushAllPreheaders = true;
}
- defExec.Push(pLoopDsc->lpEntry);
}
else // More than one exit
{
- JITDUMP(" only considering hoisting in entry block " FMT_BB "\n", pLoopDsc->lpEntry->bbNum);
// We'll assume that only the entry block is definitely executed.
// We could in the future do better.
- defExec.Push(pLoopDsc->lpEntry);
+
+ JITDUMP(" Considering hoisting in entry block " FMT_BB " because " FMT_LP " has more than one exit\n",
+ pLoopDsc->lpEntry->bbNum, lnum);
+ pushAllPreheaders = true;
}
+ if (pushAllPreheaders)
+ {
+ // We will still push all the preheaders found.
+ BasicBlockList* preHeadersList = existingPreHeaders;
+
+ while (preHeadersList != nullptr)
+ {
+ BasicBlock* preHeaderBlock = preHeadersList->block;
+ JITDUMP(" -- " FMT_BB " (preheader of " FMT_LP ")\n", preHeaderBlock->bbNum,
+ optLoopEntry(preHeaderBlock)->bbNatLoopNum);
+ defExec.Push(preHeaderBlock);
+ preHeadersList = preHeadersList->next;
+ }
+ }
+
+ JITDUMP(" -- " FMT_BB " (entry block)\n", pLoopDsc->lpEntry->bbNum);
+ defExec.Push(pLoopDsc->lpEntry);
+
optHoistLoopBlocks(lnum, &defExec, hoistCtxt);
}
@@ -6765,6 +7107,24 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
return vnIsInvariant;
}
+ bool IsHoistableOverExcepSibling(GenTree* node, bool siblingHasExcep)
+ {
+ JITDUMP(" [%06u]", dspTreeID(node));
+
+ if ((node->gtFlags & GTF_ALL_EFFECT) != 0)
+ {
+ // If the hoistable node has any side effects, make sure
+ // we don't hoist it past a sibling that throws any exception.
+ if (siblingHasExcep)
+ {
+ JITDUMP(" not hoistable: cannot move past node that throws exception.\n");
+ return false;
+ }
+ }
+ JITDUMP(" hoistable\n");
+ return true;
+ }
+
//------------------------------------------------------------------------
// IsTreeLoopMemoryInvariant: determine if the value number of tree
// is dependent on the tree being executed within the current loop
@@ -6868,6 +7228,7 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
fgWalkResult PostOrderVisit(GenTree** use, GenTree* user)
{
GenTree* tree = *use;
+ JITDUMP("----- PostOrderVisit for [%06u]\n", dspTreeID(tree));
if (tree->OperIsLocal())
{
@@ -7174,6 +7535,15 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
// cctor dependent node is initially not hoistable and may become hoistable later,
// when its parent comma node is visited.
//
+ // TODO-CQ: Ideally, we should be hoisting all the nodes having side-effects in execution
+ // order as well as the ones that don't have side-effects at all. However, currently, we
+ // just restrict hoisting a node(s) (that are children of `comma`) if one of the siblings
+ // (which is executed before the given node) has side-effects (exceptions). "Descendants
+ // of ancestors might have side-effects and we might hoist nodes past them. This needs
+ // to be addressed properly.
+ bool visitedCurr = false;
+ bool isCommaTree = tree->OperIs(GT_COMMA);
+ bool hasExcep = false;
for (int i = 0; i < m_valueStack.Height(); i++)
{
Value& value = m_valueStack.BottomRef(i);
@@ -7182,17 +7552,32 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
{
assert(value.Node() != tree);
+ if (IsHoistableOverExcepSibling(value.Node(), hasExcep))
+ {
+ m_compiler->optHoistCandidate(value.Node(), m_currentBlock, m_loopNum, m_hoistContext);
+ }
+
// Don't hoist this tree again.
value.m_hoistable = false;
value.m_invariant = false;
-
- m_compiler->optHoistCandidate(value.Node(), m_currentBlock, m_loopNum, m_hoistContext);
}
else if (value.Node() != tree)
{
+ if (visitedCurr && isCommaTree)
+ {
+ // If we have visited current tree, now we are visiting children.
+ // For GT_COMMA nodes, we want to track if any children throws and
+ // should not hoist further children past it.
+ hasExcep = (tree->gtFlags & GTF_EXCEPT) != 0;
+ }
JITDUMP(" [%06u] not %s: %s\n", dspTreeID(value.Node()),
value.m_invariant ? "invariant" : "hoistable", value.m_failReason);
}
+ else
+ {
+ visitedCurr = true;
+ JITDUMP(" [%06u] not hoistable : current node\n", dspTreeID(value.Node()));
+ }
}
}
@@ -7236,6 +7621,8 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
visitor.HoistBlock(block);
}
+
+ hoistContext->ResetHoistedInCurLoop();
}
void Compiler::optHoistCandidate(GenTree* tree, BasicBlock* treeBb, unsigned lnum, LoopHoistContext* hoistCtxt)
@@ -7249,17 +7636,12 @@ void Compiler::optHoistCandidate(GenTree* tree, BasicBlock* treeBb, unsigned lnu
return;
}
- if (hoistCtxt->m_hoistedInParentLoops.Lookup(tree->gtVNPair.GetLiberal()))
- {
- JITDUMP(" ... already hoisted same VN in parent\n");
- // already hoisted in a parent loop, so don't hoist this expression.
- return;
- }
-
if (hoistCtxt->GetHoistedInCurLoop(this)->Lookup(tree->gtVNPair.GetLiberal()))
{
- JITDUMP(" ... already hoisted same VN in current\n");
// already hoisted this expression in the current loop, so don't hoist this expression.
+
+ JITDUMP(" [%06u] ... already hoisted " FMT_VN " in " FMT_LP "\n ", dspTreeID(tree),
+ tree->gtVNPair.GetLiberal(), lnum);
return;
}
diff --git a/src/coreclr/jit/patchpoint.cpp b/src/coreclr/jit/patchpoint.cpp
index d1478c51c31239..bf7560dd431a1f 100644
--- a/src/coreclr/jit/patchpoint.cpp
+++ b/src/coreclr/jit/patchpoint.cpp
@@ -78,7 +78,7 @@ class PatchpointTransformer
// If we're instrumenting, we should not have decided to
// put class probes here, as that is driven by looking at IL.
//
- assert((block->bbFlags & BBF_HAS_CLASS_PROFILE) == 0);
+ assert((block->bbFlags & BBF_HAS_HISTOGRAM_PROFILE) == 0);
// Clear the partial comp flag.
//
diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp
index 970e799d9b509b..6b1575ab5f928f 100644
--- a/src/coreclr/jit/rangecheck.cpp
+++ b/src/coreclr/jit/rangecheck.cpp
@@ -947,14 +947,14 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool
return range;
}
// Generalized range computation not implemented for these operators
- else if (binop->OperIs(GT_AND, GT_UMOD, GT_RSH))
+ else if (binop->OperIs(GT_AND, GT_UMOD))
{
return Range(Limit::keUnknown);
}
}
// other operators are expected to be handled above.
- assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH));
+ assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH, GT_RSH));
Range* op1RangeCached = nullptr;
Range op1Range = Limit(Limit::keUndef);
@@ -1024,9 +1024,43 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool
convertedOp2Range.ToString(m_pCompiler->getAllocatorDebugOnly()),
r.ToString(m_pCompiler->getAllocatorDebugOnly()));
}
+ else if (binop->OperIs(GT_RSH))
+ {
+ r = RangeOps::ShiftRight(op1Range, op2Range);
+ JITDUMP("Right shift range: %s >> %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()),
+ op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()),
+ r.ToString(m_pCompiler->getAllocatorDebugOnly()));
+ }
return r;
}
+//------------------------------------------------------------------------
+// GetRangeFromType: Compute the range from the given type
+//
+// Arguments:
+// type - input type
+//
+// Return value:
+// range that represents the values given type allows
+//
+Range RangeCheck::GetRangeFromType(var_types type)
+{
+ switch (type)
+ {
+ case TYP_BOOL:
+ case TYP_UBYTE:
+ return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, BYTE_MAX));
+ case TYP_BYTE:
+ return Range(Limit(Limit::keConstant, INT8_MIN), Limit(Limit::keConstant, INT8_MAX));
+ case TYP_USHORT:
+ return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, UINT16_MAX));
+ case TYP_SHORT:
+ return Range(Limit(Limit::keConstant, INT16_MIN), Limit(Limit::keConstant, INT16_MAX));
+ default:
+ return Range(Limit(Limit::keUnknown));
+ }
+}
+
// Compute the range for a local var definition.
Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block,
GenTreeLclVarCommon* lcl,
@@ -1242,11 +1276,11 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
{
overflows = false;
}
- else if (expr->OperGet() == GT_IND)
+ else if (expr->OperIs(GT_IND))
{
overflows = false;
}
- else if (expr->OperGet() == GT_COMMA)
+ else if (expr->OperIs(GT_COMMA))
{
overflows = ComputeDoesOverflow(block, expr->gtEffectiveVal());
}
@@ -1256,7 +1290,7 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
overflows = DoesVarDefOverflow(expr->AsLclVarCommon());
}
// Check if add overflows.
- else if (expr->OperGet() == GT_ADD || expr->OperGet() == GT_MUL)
+ else if (expr->OperIs(GT_ADD, GT_MUL))
{
overflows = DoesBinOpOverflow(block, expr->AsOp());
}
@@ -1267,10 +1301,14 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr)
overflows = false;
}
// Walk through phi arguments to check if phi arguments involve arithmetic that overflows.
- else if (expr->OperGet() == GT_PHI)
+ else if (expr->OperIs(GT_PHI))
{
overflows = DoesPhiOverflow(block, expr);
}
+ else if (expr->OperIs(GT_CAST))
+ {
+ overflows = ComputeDoesOverflow(block, expr->gtGetOp1());
+ }
GetOverflowMap()->Set(expr, overflows, OverflowMap::Overwrite);
m_pSearchPath->Remove(expr);
JITDUMP("[%06d] %s\n", Compiler::dspTreeID(expr), ((overflows) ? "overflows" : "does not overflow"));
@@ -1357,7 +1395,7 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas
range = ComputeRangeForBinOp(block, expr->AsOp(), monIncreasing DEBUGARG(indent + 1));
}
// If phi, then compute the range for arguments, calling the result "dependent" when looping begins.
- else if (expr->OperGet() == GT_PHI)
+ else if (expr->OperIs(GT_PHI))
{
for (GenTreePhi::Use& use : expr->AsPhi()->Uses())
{
@@ -1382,31 +1420,20 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas
}
else if (varTypeIsSmallInt(expr->TypeGet()))
{
- switch (expr->TypeGet())
- {
- case TYP_UBYTE:
- range = Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, 255));
- break;
- case TYP_BYTE:
- range = Range(Limit(Limit::keConstant, -128), Limit(Limit::keConstant, 127));
- break;
- case TYP_USHORT:
- range = Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, 65535));
- break;
- case TYP_SHORT:
- range = Range(Limit(Limit::keConstant, -32768), Limit(Limit::keConstant, 32767));
- break;
- default:
- range = Range(Limit(Limit::keUnknown));
- break;
- }
-
+ range = GetRangeFromType(expr->TypeGet());
JITDUMP("%s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly()));
}
- else if (expr->OperGet() == GT_COMMA)
+ else if (expr->OperIs(GT_COMMA))
{
range = GetRange(block, expr->gtEffectiveVal(), monIncreasing DEBUGARG(indent + 1));
}
+ else if (expr->OperIs(GT_CAST))
+ {
+ GenTreeCast* castTree = expr->AsCast();
+ // TODO: consider computing range for CastOp and intersect it
+ // with this
+ range = GetRangeFromType(castTree->CastToType());
+ }
else
{
// The expression is not recognized, so the result is unknown.
diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h
index 49c5a2950a939d..db1c25a09eda54 100644
--- a/src/coreclr/jit/rangecheck.h
+++ b/src/coreclr/jit/rangecheck.h
@@ -101,27 +101,27 @@ struct Limit
assert(type == keBinOpArray);
}
- bool IsUndef()
+ bool IsUndef() const
{
return type == keUndef;
}
- bool IsDependent()
+ bool IsDependent() const
{
return type == keDependent;
}
- bool IsUnknown()
+ bool IsUnknown() const
{
return type == keUnknown;
}
- bool IsConstant()
+ bool IsConstant() const
{
return type == keConstant;
}
- int GetConstant()
+ int GetConstant() const
{
return cns;
}
- bool IsBinOpArray()
+ bool IsBinOpArray() const
{
return type == keBinOpArray;
}
@@ -170,6 +170,27 @@ struct Limit
return false;
}
+ bool ShiftRightConstant(int i)
+ {
+ switch (type)
+ {
+ case keDependent:
+ return true;
+ case keBinOpArray:
+ case keConstant:
+ // >> never overflows
+ assert((unsigned)i <= 31);
+ cns >>= i;
+ return true;
+ case keUndef:
+ case keUnknown:
+ // For these values of 'type', conservatively return false
+ break;
+ }
+
+ return false;
+ }
+
bool Equals(Limit& l)
{
switch (type)
@@ -257,34 +278,40 @@ struct Range
// Helpers for operations performed on ranges
struct RangeOps
{
- // Given a constant limit in "l1", add it to l2 and mutate "l2".
- static Limit AddConstantLimit(Limit& l1, Limit& l2)
+ // Perform 'value' + 'cns'
+ static Limit AddConstantLimit(const Limit& value, const Limit& cns)
{
- assert(l1.IsConstant());
- Limit l = l2;
- if (l.AddConstant(l1.GetConstant()))
+ assert(cns.IsConstant());
+ Limit l = value;
+ if (l.AddConstant(cns.GetConstant()))
{
return l;
}
- else
- {
- return Limit(Limit::keUnknown);
- }
+ return Limit(Limit::keUnknown);
}
- // Given a constant limit in "l1", multiply it to l2 and mutate "l2".
- static Limit MultiplyConstantLimit(Limit& l1, Limit& l2)
+ // Perform 'value' * 'cns'
+ static Limit MultiplyConstantLimit(const Limit& value, const Limit& cns)
{
- assert(l1.IsConstant());
- Limit l = l2;
- if (l.MultiplyConstant(l1.GetConstant()))
+ assert(cns.IsConstant());
+ Limit l = value;
+ if (l.MultiplyConstant(cns.GetConstant()))
{
return l;
}
- else
+ return Limit(Limit::keUnknown);
+ }
+
+ // Perform 'value' >> 'cns'
+ static Limit ShiftRightConstantLimit(const Limit& value, const Limit& cns)
+ {
+ assert(value.IsConstant());
+ Limit result = value;
+ if (result.ShiftRightConstant(cns.GetConstant()))
{
- return Limit(Limit::keUnknown);
+ return result;
}
+ return Limit(Limit::keUnknown);
}
// Given two ranges "r1" and "r2", perform an add operation on the
@@ -311,20 +338,57 @@ struct RangeOps
if (r1lo.IsConstant())
{
- result.lLimit = AddConstantLimit(r1lo, r2lo);
+ result.lLimit = AddConstantLimit(r2lo, r1lo);
}
if (r2lo.IsConstant())
{
- result.lLimit = AddConstantLimit(r2lo, r1lo);
+ result.lLimit = AddConstantLimit(r1lo, r2lo);
}
if (r1hi.IsConstant())
{
- result.uLimit = AddConstantLimit(r1hi, r2hi);
+ result.uLimit = AddConstantLimit(r2hi, r1hi);
}
if (r2hi.IsConstant())
{
- result.uLimit = AddConstantLimit(r2hi, r1hi);
+ result.uLimit = AddConstantLimit(r1hi, r2hi);
+ }
+ return result;
+ }
+
+ static Range ShiftRight(Range& r1, Range& r2)
+ {
+ Limit& r1lo = r1.LowerLimit();
+ Limit& r1hi = r1.UpperLimit();
+ Limit& r2lo = r2.LowerLimit();
+ Limit& r2hi = r2.UpperLimit();
+
+ Range result = Limit(Limit::keUnknown);
+
+ // For now we only support r1 >> positive_cns (to simplify)
+ if (!r2lo.IsConstant() || !r2hi.IsConstant() || (r2lo.cns < 0) || (r2hi.cns < 0))
+ {
+ return result;
+ }
+
+ // Check lo ranges if they are dependent and not unknown.
+ if (r1lo.IsDependent())
+ {
+ result.lLimit = Limit(Limit::keDependent);
+ }
+ else if (r1lo.IsConstant())
+ {
+ result.lLimit = ShiftRightConstantLimit(r1lo, r2lo);
+ }
+
+ if (r1hi.IsDependent())
+ {
+ result.uLimit = Limit(Limit::keDependent);
}
+ else if (r1hi.IsConstant())
+ {
+ result.uLimit = ShiftRightConstantLimit(r1hi, r2hi);
+ }
+
return result;
}
@@ -352,19 +416,19 @@ struct RangeOps
if (r1lo.IsConstant())
{
- result.lLimit = MultiplyConstantLimit(r1lo, r2lo);
+ result.lLimit = MultiplyConstantLimit(r2lo, r1lo);
}
if (r2lo.IsConstant())
{
- result.lLimit = MultiplyConstantLimit(r2lo, r1lo);
+ result.lLimit = MultiplyConstantLimit(r1lo, r2lo);
}
if (r1hi.IsConstant())
{
- result.uLimit = MultiplyConstantLimit(r1hi, r2hi);
+ result.uLimit = MultiplyConstantLimit(r2hi, r1hi);
}
if (r2hi.IsConstant())
{
- result.uLimit = MultiplyConstantLimit(r2hi, r1hi);
+ result.uLimit = MultiplyConstantLimit(r1hi, r2hi);
}
return result;
}
@@ -560,6 +624,9 @@ class RangeCheck
// at phi definitions for the lower bound.
Range GetRange(BasicBlock* block, GenTree* expr, bool monIncreasing DEBUGARG(int indent));
+ // Compute the range from the given type
+ Range GetRangeFromType(var_types type);
+
// Given the local variable, first find the definition of the local and find the range of the rhs.
// Helper for GetRange.
Range ComputeRangeForLocalDef(BasicBlock* block, GenTreeLclVarCommon* lcl, bool monIncreasing DEBUGARG(int indent));
diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp
index e6b7e1a4d9234d..4cb2d7261fab99 100644
--- a/src/coreclr/jit/rationalize.cpp
+++ b/src/coreclr/jit/rationalize.cpp
@@ -134,23 +134,6 @@ void Rationalizer::RewriteSIMDIndir(LIR::Use& use)
addr->gtType = simdType;
use.ReplaceWith(addr);
}
- else if (addr->OperIs(GT_ADDR))
- {
- GenTree* location = addr->AsUnOp()->gtGetOp1();
-
- if (location->OperIsSimdOrHWintrinsic() || location->IsCnsVec())
- {
- // If we have IND(ADDR(SIMD)) then we can keep only the SIMD node.
- // This is a special tree created by impNormStructVal to preserve the class layout
- // needed by call morphing on an OBJ node. This information is no longer needed at
- // this point (and the address of a SIMD node can't be obtained anyway).
-
- BlockRange().Remove(indir);
- BlockRange().Remove(addr);
-
- use.ReplaceWith(addr->AsUnOp()->gtGetOp1());
- }
- }
#endif // FEATURE_SIMD
}
@@ -265,6 +248,62 @@ void Rationalizer::RewriteIntrinsicAsUserCall(GenTree** use, ArrayStack a % cns
+// where cns is a signed integer constant that is a power of 2.
+// We do this transformation because Lowering has a specific optimization
+// for 'a % cns' that is not easily reduced by other means.
+//
+void Rationalizer::RewriteSubLshDiv(GenTree** use)
+{
+ if (!comp->opts.OptimizationEnabled())
+ return;
+
+ GenTree* const node = *use;
+
+ if (!node->OperIs(GT_SUB))
+ return;
+
+ GenTree* op1 = node->gtGetOp1();
+ GenTree* op2 = node->gtGetOp2();
+
+ if (!(node->TypeIs(TYP_INT, TYP_LONG) && op1->OperIs(GT_LCL_VAR)))
+ return;
+
+ if (!op2->OperIs(GT_LSH))
+ return;
+
+ GenTree* lsh = op2;
+ GenTree* div = lsh->gtGetOp1();
+ GenTree* shift = lsh->gtGetOp2();
+ if (div->OperIs(GT_DIV) && shift->IsIntegralConst())
+ {
+ GenTree* a = div->gtGetOp1();
+ GenTree* cns = div->gtGetOp2();
+ if (a->OperIs(GT_LCL_VAR) && cns->IsIntegralConstPow2() &&
+ op1->AsLclVar()->GetLclNum() == a->AsLclVar()->GetLclNum())
+ {
+ size_t shiftValue = shift->AsIntConCommon()->IntegralValue();
+ size_t cnsValue = cns->AsIntConCommon()->IntegralValue();
+ if ((cnsValue >> shiftValue) == 1)
+ {
+ node->ChangeOper(GT_MOD);
+ node->AsOp()->gtOp2 = cns;
+ BlockRange().Remove(lsh);
+ BlockRange().Remove(div);
+ BlockRange().Remove(a);
+ BlockRange().Remove(shift);
+ }
+ }
+ }
+}
+#endif
+
#ifdef DEBUG
void Rationalizer::ValidateStatement(Statement* stmt, BasicBlock* block)
@@ -524,6 +563,10 @@ void Rationalizer::RewriteAddress(LIR::Use& use)
JITDUMP("Rewriting GT_ADDR(GT_IND(X)) to X:\n");
}
+ else
+ {
+ unreached();
+ }
DISPTREERANGE(BlockRange(), use.Def());
JITDUMP("\n");
@@ -556,13 +599,6 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
RewriteAssignment(use);
break;
- case GT_BOX:
- case GT_ARR_ADDR:
- // BOX/ARR_ADDR at this level are just NOPs.
- use.ReplaceWith(node->gtGetOp1());
- BlockRange().Remove(node);
- break;
-
case GT_ADDR:
RewriteAddress(use);
break;
@@ -594,9 +630,11 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
break;
case GT_NOP:
- // fgMorph sometimes inserts NOP nodes between defs and uses
- // supposedly 'to prevent constant folding'. In this case, remove the
- // NOP.
+ case GT_BOX:
+ case GT_ARR_ADDR:
+ // "optNarrowTree" sometimes inserts NOP nodes between defs and uses.
+ // In this case, remove the NOP. BOX/ARR_ADDR are such "passthrough"
+ // nodes by design, and at this point we no longer need them.
if (node->gtGetOp1() != nullptr)
{
use.ReplaceWith(node->gtGetOp1());
@@ -780,6 +818,13 @@ PhaseStatus Rationalizer::DoPhase()
m_rationalizer.RewriteIntrinsicAsUserCall(use, this->m_ancestors);
}
+#ifdef TARGET_ARM64
+ if (node->OperIs(GT_SUB))
+ {
+ m_rationalizer.RewriteSubLshDiv(use);
+ }
+#endif
+
return Compiler::WALK_CONTINUE;
}
diff --git a/src/coreclr/jit/rationalize.h b/src/coreclr/jit/rationalize.h
index 47d355cbbd38f4..329fbcbd1ac248 100644
--- a/src/coreclr/jit/rationalize.h
+++ b/src/coreclr/jit/rationalize.h
@@ -58,6 +58,10 @@ class Rationalizer final : public Phase
void RewriteAssignment(LIR::Use& use);
void RewriteAddress(LIR::Use& use);
+#ifdef TARGET_ARM64
+ void RewriteSubLshDiv(GenTree** use);
+#endif
+
// Root visitor
Compiler::fgWalkResult RewriteNode(GenTree** useEdge, Compiler::GenTreeStack& parents);
};
diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp
index 6b2fcd5690eb6e..b349d5e7aae64e 100644
--- a/src/coreclr/jit/scopeinfo.cpp
+++ b/src/coreclr/jit/scopeinfo.cpp
@@ -295,7 +295,7 @@ void CodeGenInterface::siVarLoc::siFillStackVarLoc(
case TYP_LONG:
case TYP_DOUBLE:
#endif // TARGET_64BIT
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#if FEATURE_IMPLICIT_BYREFS
// In the AMD64 ABI we are supposed to pass a struct by reference when its
// size is not 1, 2, 4 or 8 bytes in size. During fgMorph, the compiler modifies
// the IR to comply with the ABI and therefore changes the type of the lclVar
@@ -314,7 +314,7 @@ void CodeGenInterface::siVarLoc::siFillStackVarLoc(
this->vlType = VLT_STK_BYREF;
}
else
-#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+#endif // FEATURE_IMPLICIT_BYREFS
{
this->vlType = VLT_STK;
}
diff --git a/src/coreclr/jit/simd.cpp b/src/coreclr/jit/simd.cpp
index 92706856a0dce8..f0fc16be21c67f 100644
--- a/src/coreclr/jit/simd.cpp
+++ b/src/coreclr/jit/simd.cpp
@@ -1207,7 +1207,7 @@ const SIMDIntrinsicInfo* Compiler::getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* in
}
// Pops and returns GenTree node from importer's type stack.
-// Normalizes TYP_STRUCT value in case of GT_CALL, GT_RET_EXPR and arg nodes.
+// Normalizes TYP_STRUCT value in case of GT_CALL and GT_RET_EXPR.
//
// Arguments:
// type - the type of value that the caller expects to be popped off the stack.
@@ -1219,7 +1219,7 @@ const SIMDIntrinsicInfo* Compiler::getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* in
// Notes:
// If the popped value is a struct, and the expected type is a simd type, it will be set
// to that type, otherwise it will assert if the type being popped is not the expected type.
-
+//
GenTree* Compiler::impSIMDPopStack(var_types type, bool expectAddr, CORINFO_CLASS_HANDLE structHandle)
{
StackEntry se = impPopStack();
@@ -1232,54 +1232,25 @@ GenTree* Compiler::impSIMDPopStack(var_types type, bool expectAddr, CORINFO_CLAS
{
assert(tree->TypeIs(TYP_BYREF, TYP_I_IMPL));
- if (tree->OperGet() == GT_ADDR)
- {
- tree = tree->gtGetOp1();
- }
- else
- {
- tree = gtNewOperNode(GT_IND, type, tree);
- }
+ tree = gtNewOperNode(GT_IND, type, tree);
}
- bool isParam = false;
-
- // If we are popping a struct type it must have a matching handle if one is specified.
- // - If we have an existing 'OBJ' and 'structHandle' is specified, we will change its
- // handle if it doesn't match.
- // This can happen when we have a retyping of a vector that doesn't translate to any
- // actual IR.
- // - (If it's not an OBJ and it's used in a parameter context where it is required,
- // impNormStructVal will add one).
- //
- if (tree->OperGet() == GT_OBJ)
+ if (tree->OperIsIndir() && tree->AsIndir()->Addr()->OperIs(GT_ADDR))
{
- if ((structHandle != NO_CLASS_HANDLE) && (tree->AsObj()->GetLayout()->GetClassHandle() != structHandle))
+ GenTree* location = tree->AsIndir()->Addr()->gtGetOp1();
+ if (location->OperIs(GT_LCL_VAR) && location->TypeIs(type))
{
- // In this case we need to retain the GT_OBJ to retype the value.
- tree->AsObj()->SetLayout(typGetObjLayout(structHandle));
+ assert(type != TYP_STRUCT);
+ tree = location;
}
- else
- {
- GenTree* addr = tree->AsOp()->gtOp1;
- if ((addr->OperGet() == GT_ADDR) && isSIMDTypeLocal(addr->AsOp()->gtOp1))
- {
- tree = addr->AsOp()->gtOp1;
- }
- }
- }
-
- if (tree->OperGet() == GT_LCL_VAR)
- {
- isParam = lvaGetDesc(tree->AsLclVarCommon())->lvIsParam;
}
- // normalize TYP_STRUCT value
- if (varTypeIsStruct(tree) && ((tree->OperGet() == GT_RET_EXPR) || (tree->OperGet() == GT_CALL) || isParam))
+ // Handle calls that may return the struct via a return buffer.
+ if (varTypeIsStruct(tree) && tree->OperIs(GT_CALL, GT_RET_EXPR))
{
assert(ti.IsType(TI_STRUCT));
- if (structHandle == nullptr)
+ if (structHandle == NO_CLASS_HANDLE)
{
structHandle = ti.GetClassHandleForValueClass();
}
@@ -1685,7 +1656,15 @@ bool Compiler::areArrayElementsContiguous(GenTree* op1, GenTree* op2)
//
bool Compiler::areArgumentsContiguous(GenTree* op1, GenTree* op2)
{
- if (op1->OperIs(GT_IND) && op2->OperIs(GT_IND))
+ if (op1->TypeGet() != op2->TypeGet())
+ {
+ return false;
+ }
+
+ assert(!op1->TypeIs(TYP_STRUCT));
+
+ if (op1->OperIs(GT_IND) && op1->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR) && op2->OperIs(GT_IND) &&
+ op2->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR))
{
return areArrayElementsContiguous(op1, op2);
}
diff --git a/src/coreclr/jit/targetamd64.h b/src/coreclr/jit/targetamd64.h
index ee2868fef42388..645d1933e3c5a4 100644
--- a/src/coreclr/jit/targetamd64.h
+++ b/src/coreclr/jit/targetamd64.h
@@ -34,6 +34,7 @@
#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
#define MAX_PASS_SINGLEREG_BYTES 8 // Maximum size of a struct passed in a single register (double).
#ifdef UNIX_AMD64_ABI
+ #define FEATURE_IMPLICIT_BYREFS 0 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register
@@ -48,6 +49,7 @@
// This is also the maximum number of registers for a MultiReg node.
#else // !UNIX_AMD64_ABI
#define WINDOWS_AMD64_ABI // Uses the Windows ABI for AMD64
+ #define FEATURE_IMPLICIT_BYREFS 1 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 0 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 0 // Support for returning a single value in more than one register
diff --git a/src/coreclr/jit/targetarm.h b/src/coreclr/jit/targetarm.h
index 8e93c1be084409..3e43eaa587141c 100644
--- a/src/coreclr/jit/targetarm.h
+++ b/src/coreclr/jit/targetarm.h
@@ -23,6 +23,7 @@
#define FEATURE_FASTTAILCALL 1 // Tail calls made as epilog+jmp
#define FEATURE_TAILCALL_OPT 1 // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
#define FEATURE_SET_FLAGS 1 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
+ #define FEATURE_IMPLICIT_BYREFS 0 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register (including HFA support)
#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register (including passing HFAs)
#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register (including HFA returns)
diff --git a/src/coreclr/jit/targetarm64.h b/src/coreclr/jit/targetarm64.h
index dbfd813ac090c6..c9a1610aab2c5c 100644
--- a/src/coreclr/jit/targetarm64.h
+++ b/src/coreclr/jit/targetarm64.h
@@ -28,6 +28,7 @@
#define FEATURE_FASTTAILCALL 1 // Tail calls made as epilog+jmp
#define FEATURE_TAILCALL_OPT 1 // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
+ #define FEATURE_IMPLICIT_BYREFS 1 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register
diff --git a/src/coreclr/jit/targetloongarch64.h b/src/coreclr/jit/targetloongarch64.h
index f704b4b256afbf..4190420dc5b691 100644
--- a/src/coreclr/jit/targetloongarch64.h
+++ b/src/coreclr/jit/targetloongarch64.h
@@ -31,6 +31,7 @@
#define FEATURE_FASTTAILCALL 1 // Tail calls made as epilog+jmp
#define FEATURE_TAILCALL_OPT 1 // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
+ #define FEATURE_IMPLICIT_BYREFS 1 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register
diff --git a/src/coreclr/jit/targetx86.h b/src/coreclr/jit/targetx86.h
index 044b9f201d5e32..727275654930f1 100644
--- a/src/coreclr/jit/targetx86.h
+++ b/src/coreclr/jit/targetx86.h
@@ -30,6 +30,7 @@
#define FEATURE_TAILCALL_OPT 0 // opportunistic Tail calls (without ".tail" prefix) made as fast tail calls.
#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when
// the flags need to be set
+ #define FEATURE_IMPLICIT_BYREFS 0 // Support for struct parameters passed via pointers to shadow copies
#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register
diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp
index 6ad60a064f35c9..63c4ed716cf390 100644
--- a/src/coreclr/jit/unwind.cpp
+++ b/src/coreclr/jit/unwind.cpp
@@ -69,7 +69,16 @@ void Compiler::unwindGetFuncLocations(FuncInfoDsc* func,
// The hot section only goes up to the cold section
assert(fgFirstFuncletBB == nullptr);
- *ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstColdBlock));
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting())
+ {
+ *ppEndLoc = nullptr; // If fake-splitting, "trick" VM by pretending entire function is hot.
+ }
+ else
+#endif // DEBUG
+ {
+ *ppEndLoc = new (this, CMK_UnwindInfo) emitLocation(ehEmitCookie(fgFirstColdBlock));
+ }
}
else
{
@@ -259,6 +268,13 @@ void Compiler::unwindEmitFuncCFI(FuncInfoDsc* func, void* pHotCode, void* pColdC
DWORD unwindCodeBytes = 0;
BYTE* pUnwindBlock = nullptr;
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting())
+ {
+ pColdCode = nullptr;
+ }
+#endif // DEBUG
+
if (func->startLoc == nullptr)
{
startOffset = 0;
diff --git a/src/coreclr/jit/unwindamd64.cpp b/src/coreclr/jit/unwindamd64.cpp
index 2c8e90fa5a944d..88cefbe31ed5e9 100644
--- a/src/coreclr/jit/unwindamd64.cpp
+++ b/src/coreclr/jit/unwindamd64.cpp
@@ -656,18 +656,17 @@ void Compiler::unwindReserve()
//
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
{
-#ifdef DEBUG
- if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
+ unwindReserveFuncHelper(func, true);
+
+ if (fgFirstColdBlock != nullptr)
{
- assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
- unwindReserveFuncHelper(func, true);
- }
- else
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting())
+ {
+ assert(func->funKind == FUNC_ROOT); // No splitting of funclets.
+ }
+ else
#endif // DEBUG
- {
- unwindReserveFuncHelper(func, true);
-
- if (fgFirstColdBlock != nullptr)
{
unwindReserveFuncHelper(func, false);
}
@@ -859,7 +858,17 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
if (isHotCode)
{
- assert(endOffset <= info.compTotalHotCodeSize);
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
+ {
+ assert(endOffset <= info.compNativeCodeSize);
+ }
+ else
+#endif // DEBUG
+ {
+ assert(endOffset <= info.compTotalHotCodeSize);
+ }
+
pColdCode = nullptr;
}
else
@@ -890,43 +899,17 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER);
static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER);
-#ifdef DEBUG
- if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != nullptr))
+ unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
+
+ if (pColdCode != nullptr)
{
- fakeUnwindEmitFuncHelper(func, pHotCode);
- }
- else
+#ifdef DEBUG
+ if (!JitConfig.JitFakeProcedureSplitting())
#endif // DEBUG
- {
- unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
-
- if (pColdCode != nullptr)
{
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
}
}
}
-#ifdef DEBUG
-void Compiler::fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode)
-{
- assert(fgFirstColdBlock != nullptr);
- assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
-
- const UNATIVE_OFFSET startOffset = 0;
- const UNATIVE_OFFSET endOffset = info.compNativeCodeSize;
- const DWORD unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot;
- BYTE* pUnwindBlock = &func->unwindCodes[func->unwindCodeSlot];
-
- if (opts.dspUnwind)
- {
- DumpUnwindInfo(true, startOffset, endOffset, (const UNWIND_INFO* const)pUnwindBlock);
- }
-
- // Pass pColdCode = nullptr; VM allocs unwind info for combined hot/cold section
- eeAllocUnwindInfo((BYTE*)pHotCode, nullptr, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
- (CorJitFuncKind)func->funKind);
-}
-#endif // DEBUG
-
#endif // TARGET_AMD64
diff --git a/src/coreclr/jit/unwindarm.cpp b/src/coreclr/jit/unwindarm.cpp
index 1eb7456250cbb5..8a14c6edbb8324 100644
--- a/src/coreclr/jit/unwindarm.cpp
+++ b/src/coreclr/jit/unwindarm.cpp
@@ -563,13 +563,20 @@ void Compiler::unwindReserve()
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
{
BOOL isFunclet = (func->funKind == FUNC_ROOT) ? FALSE : TRUE;
- bool funcHasColdSection = false;
+ bool funcHasColdSection = (fgFirstColdBlock != nullptr);
+
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting() && funcHasColdSection)
+ {
+ funcHasColdSection = false; // "Trick" the VM into thinking we don't have a cold section.
+ }
+#endif // DEBUG
#if defined(FEATURE_CFI_SUPPORT)
if (generateCFIUnwindCodes())
{
DWORD unwindCodeBytes = 0;
- if (fgFirstColdBlock != nullptr)
+ if (funcHasColdSection)
{
eeReserveUnwindInfo(isFunclet, true /*isColdCode*/, unwindCodeBytes);
}
@@ -584,7 +591,7 @@ void Compiler::unwindReserveFunc(FuncInfoDsc* func)
// cold section. This needs to be done before we split into fragments, as each
// of the hot and cold sections can have multiple fragments.
- if (fgFirstColdBlock != NULL)
+ if (funcHasColdSection)
{
assert(!isFunclet); // TODO-CQ: support hot/cold splitting with EH
@@ -595,8 +602,6 @@ void Compiler::unwindReserveFunc(FuncInfoDsc* func)
func->uwiCold = new (this, CMK_UnwindInfo) UnwindInfo();
func->uwiCold->InitUnwindInfo(this, startLoc, endLoc);
func->uwiCold->HotColdSplitCodes(&func->uwi);
-
- funcHasColdSection = true;
}
// First we need to split the function or funclet into fragments that are no larger
@@ -1604,11 +1609,19 @@ void UnwindFragmentInfo::Allocate(
UNATIVE_OFFSET endOffset;
UNATIVE_OFFSET codeSize;
- // We don't support hot/cold splitting with EH, so if there is cold code, this
- // better not be a funclet!
- // TODO-CQ: support funclets in cold code
-
- noway_assert(isHotCode || funKind == CORJIT_FUNC_ROOT);
+// We don't support hot/cold splitting with EH, so if there is cold code, this
+// better not be a funclet!
+// TODO-CQ: support funclets in cold code
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != NULL))
+ {
+ noway_assert(isHotCode && (funKind == CORJIT_FUNC_ROOT));
+ }
+ else
+#endif // DEBUG
+ {
+ noway_assert(isHotCode || (funKind == CORJIT_FUNC_ROOT));
+ }
// Compute the final size, and start and end offsets of the fragment
@@ -1656,7 +1669,17 @@ void UnwindFragmentInfo::Allocate(
if (isHotCode)
{
- assert(endOffset <= uwiComp->info.compTotalHotCodeSize);
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != NULL))
+ {
+ assert(endOffset <= uwiComp->info.compNativeCodeSize);
+ }
+ else
+#endif // DEBUG
+ {
+ assert(endOffset <= uwiComp->info.compTotalHotCodeSize);
+ }
+
pColdCode = NULL;
}
else
diff --git a/src/coreclr/jit/unwindx86.cpp b/src/coreclr/jit/unwindx86.cpp
index bd27e46cbef493..32d077429af6a1 100644
--- a/src/coreclr/jit/unwindx86.cpp
+++ b/src/coreclr/jit/unwindx86.cpp
@@ -113,18 +113,17 @@ void Compiler::unwindEmit(void* pHotCode, void* pColdCode)
//
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
{
-#ifdef DEBUG
- if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
+ unwindReserveFuncHelper(func, true);
+
+ if (fgFirstColdBlock != nullptr)
{
- assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
- unwindReserveFuncHelper(func, true);
- }
- else
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting())
+ {
+ assert(func->funKind == FUNC_ROOT); // No splitting of funclets.
+ }
+ else
#endif // DEBUG
- {
- unwindReserveFuncHelper(func, true);
-
- if (fgFirstColdBlock != nullptr)
{
unwindReserveFuncHelper(func, false);
}
@@ -164,17 +163,13 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER);
static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER);
-#ifdef DEBUG
- if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != nullptr))
+ unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
+
+ if (pColdCode != nullptr)
{
- fakeUnwindEmitFuncHelper(func, pHotCode);
- }
- else
+#ifdef DEBUG
+ if (!JitConfig.JitFakeProcedureSplitting())
#endif // DEBUG
- {
- unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
-
- if (pColdCode != nullptr)
{
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
}
@@ -258,7 +253,17 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
if (isHotCode)
{
- assert(endOffset <= info.compTotalHotCodeSize);
+#ifdef DEBUG
+ if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
+ {
+ assert(endOffset <= info.compNativeCodeSize);
+ }
+ else
+#endif // DEBUG
+ {
+ assert(endOffset <= info.compTotalHotCodeSize);
+ }
+
pColdCode = nullptr;
}
else
@@ -276,22 +281,4 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
(BYTE*)&unwindInfo, (CorJitFuncKind)func->funKind);
}
-#ifdef DEBUG
-void Compiler::fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode)
-{
- assert(fgFirstColdBlock != nullptr);
- assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
-
- const UNATIVE_OFFSET startOffset = 0;
- const UNATIVE_OFFSET endOffset = info.compNativeCodeSize;
-
- UNWIND_INFO unwindInfo;
- unwindInfo.FunctionLength = (ULONG)(endOffset);
-
- // Pass pColdCode = nullptr; VM allocs unwind info for combined hot/cold section
- eeAllocUnwindInfo((BYTE*)pHotCode, nullptr, startOffset, endOffset, sizeof(UNWIND_INFO), (BYTE*)&unwindInfo,
- (CorJitFuncKind)func->funKind);
-}
-#endif // DEBUG
-
#endif // FEATURE_EH_FUNCLETS
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index be7fc14611f0f8..3ec54f47e886f6 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -2869,13 +2869,15 @@ ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, Valu
{
int resVal = EvalOp(func, ConstantValue(arg0VN));
// Unary op on a handle results in a handle.
- return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetHandleFlags(arg0VN)) : VNForIntCon(resVal);
+ return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetFoldedArithOpResultHandleFlags(arg0VN))
+ : VNForIntCon(resVal);
}
case TYP_LONG:
{
INT64 resVal = EvalOp(func, ConstantValue(arg0VN));
// Unary op on a handle results in a handle.
- return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetHandleFlags(arg0VN)) : VNForLongCon(resVal);
+ return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetFoldedArithOpResultHandleFlags(arg0VN))
+ : VNForLongCon(resVal);
}
case TYP_FLOAT:
{
@@ -3106,7 +3108,7 @@ ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, Valu
ValueNum handleVN = IsVNHandle(arg0VN) ? arg0VN : IsVNHandle(arg1VN) ? arg1VN : NoVN;
if (handleVN != NoVN)
{
- result = VNForHandle(ssize_t(resultVal), GetHandleFlags(handleVN)); // Use VN for Handle
+ result = VNForHandle(ssize_t(resultVal), GetFoldedArithOpResultHandleFlags(handleVN));
}
else
{
@@ -3132,7 +3134,7 @@ ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, Valu
if (handleVN != NoVN)
{
- result = VNForHandle(ssize_t(resultVal), GetHandleFlags(handleVN)); // Use VN for Handle
+ result = VNForHandle(ssize_t(resultVal), GetFoldedArithOpResultHandleFlags(handleVN));
}
else
{
@@ -4310,14 +4312,14 @@ ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueN
// Arguments:
// block - BasicBlock where the expression that produces this value occurs.
// May be nullptr to force conservative "could be anywhere" interpretation.
-// typ - Type of the expression in the IR
+// type - Type of the expression in the IR
//
// Return Value:
// A new value number distinct from any previously generated, that compares as equal
// to itself, but not any other value number, and is annotated with the given
// type and block.
-
-ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ)
+//
+ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types type)
{
BasicBlock::loopNumber loopNum;
if (block == nullptr)
@@ -4331,7 +4333,7 @@ ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ)
// VNForFunc(typ, func, vn) but bypasses looking in the cache
//
- Chunk* const c = GetAllocChunk(typ, CEA_Func1);
+ Chunk* const c = GetAllocChunk(type, CEA_Func1);
unsigned const offsetWithinChunk = c->AllocVN();
VNDefFuncAppFlexible* fapp = c->PointerToFuncApp(offsetWithinChunk, 1);
fapp->m_func = VNF_MemOpaque;
@@ -4341,6 +4343,19 @@ ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ)
return resultVN;
}
+//------------------------------------------------------------------------
+// VNPairForExpr - Create a "new, unique" pair of value numbers.
+//
+// "VNForExpr" equivalent for "ValueNumPair"s.
+//
+ValueNumPair ValueNumStore::VNPairForExpr(BasicBlock* block, var_types type)
+{
+ ValueNum uniqVN = VNForExpr(block, type);
+ ValueNumPair uniqVNP(uniqVN, uniqVN);
+
+ return uniqVNP;
+}
+
//------------------------------------------------------------------------
// VNForLoad: Get the VN for a load from a location (physical map).
//
@@ -5119,6 +5134,37 @@ GenTreeFlags ValueNumStore::GetHandleFlags(ValueNum vn)
return handle->m_flags;
}
+GenTreeFlags ValueNumStore::GetFoldedArithOpResultHandleFlags(ValueNum vn)
+{
+ GenTreeFlags flags = GetHandleFlags(vn);
+ assert((flags & GTF_ICON_HDL_MASK) == flags);
+
+ switch (flags)
+ {
+ case GTF_ICON_SCOPE_HDL:
+ case GTF_ICON_CLASS_HDL:
+ case GTF_ICON_METHOD_HDL:
+ case GTF_ICON_FIELD_HDL:
+ case GTF_ICON_TOKEN_HDL:
+ case GTF_ICON_STR_HDL:
+ case GTF_ICON_CONST_PTR:
+ case GTF_ICON_VARG_HDL:
+ case GTF_ICON_PINVKI_HDL:
+ case GTF_ICON_FTN_ADDR:
+ case GTF_ICON_CIDMID_HDL:
+ case GTF_ICON_TLS_HDL:
+ case GTF_ICON_STATIC_BOX_PTR:
+ return GTF_ICON_CONST_PTR;
+ case GTF_ICON_STATIC_HDL:
+ case GTF_ICON_GLOBAL_PTR:
+ case GTF_ICON_BBC_PTR:
+ return GTF_ICON_GLOBAL_PTR;
+ default:
+ assert(!"Unexpected handle type");
+ return flags;
+ }
+}
+
bool ValueNumStore::IsVNHandle(ValueNum vn)
{
if (vn == NoVN)
@@ -8159,9 +8205,6 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
break;
case GT_OBJ:
- noway_assert(!"GT_OBJ can not be LHS when (tree->TypeGet() != TYP_STRUCT)!");
- break;
-
case GT_BLK:
case GT_IND:
{
@@ -8457,8 +8500,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
{
unsigned lclNum = lclFld->GetLclNum();
- // TODO-ADDR: delete the "GetSize" check once location nodes are no more.
- if (!lvaInSsa(lclFld->GetLclNum()) || !lclFld->HasSsaName() || (lclFld->GetSize() == 0))
+ if (!lvaInSsa(lclFld->GetLclNum()) || !lclFld->HasSsaName())
{
lclFld->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclFld->TypeGet()));
}
@@ -9297,143 +9339,168 @@ void Compiler::fgValueNumberSimd(GenTreeSIMD* tree)
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-// Does value-numbering for a GT_HWINTRINSIC node
void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
{
- // For safety/correctness we must mutate the global heap valuenumber
- // for any HW intrinsic that performs a memory store operation
- if (tree->OperIsMemoryStore())
- {
- fgMutateGcHeap(tree DEBUGARG("HWIntrinsic - MemoryStore"));
- }
+ NamedIntrinsic intrinsicId = tree->GetHWIntrinsicId();
+ GenTree* addr = nullptr;
+ const bool isMemoryLoad = tree->OperIsMemoryLoad(&addr);
+ const bool isMemoryStore = !isMemoryLoad && tree->OperIsMemoryStore(&addr);
- if ((tree->GetOperandCount() > 2) || ((JitConfig.JitDisableSimdVN() & 2) == 2))
+ // We do not model HWI stores precisely.
+ if (isMemoryStore)
{
- // TODO-CQ: allow intrinsics with > 2 operands to be properly VN'ed, it will
- // allow use to process things like Vector128.Create(1,2,3,4) etc.
- // Generate unique VN for now to retaing previous behavior.
- ValueNumPair vnpExcSet = vnStore->VNPForEmptyExcSet();
- for (GenTree* operand : tree->Operands())
- {
- vnpExcSet = vnStore->VNPUnionExcSet(operand->gtVNPair, vnpExcSet);
- }
- tree->gtVNPair = vnStore->VNPUniqueWithExc(tree->TypeGet(), vnpExcSet);
- return;
+ fgMutateGcHeap(tree DEBUGARG("HWIntrinsic - MemoryStore"));
}
- VNFunc func = GetVNFuncForNode(tree);
- bool isMemoryLoad = tree->OperIsMemoryLoad();
+ ValueNumPair excSetPair = ValueNumStore::VNPForEmptyExcSet();
+ ValueNumPair normalPair = ValueNumPair();
- // If we have a MemoryLoad operation we will use the fgValueNumberByrefExposedLoad
- // method to assign a value number that depends upon fgCurMemoryVN[ByrefExposed] ValueNumber
- //
- if (isMemoryLoad)
+ if ((tree->GetOperandCount() > 2) || ((JitConfig.JitDisableSimdVN() & 2) == 2))
{
- ValueNumPair op1vnp = vnStore->VNPNormalPair(tree->Op(1)->gtVNPair);
-
- // The addrVN incorporates both op1's ValueNumber and the func operation
- // The func is used because operations such as LoadLow and LoadHigh perform
- // different operations, thus need to compute different ValueNumbers
- // We don't need to encode the result type as it will be encoded by the opcode in 'func'
- // TODO-Bug: some HWI loads have more than one operand, we need to encode the rest.
- ValueNum addrVN = vnStore->VNForFunc(TYP_BYREF, func, op1vnp.GetLiberal());
-
- // The address could point anywhere, so it is an ByrefExposed load.
- //
- ValueNum loadVN = fgValueNumberByrefExposedLoad(tree->TypeGet(), addrVN);
- tree->gtVNPair.SetLiberal(loadVN);
- tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
+ // TODO-CQ: allow intrinsics with > 2 operands to be properly VN'ed.
+ normalPair = vnStore->VNPairForExpr(compCurBB, tree->TypeGet());
for (GenTree* operand : tree->Operands())
{
- tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, vnStore->VNPExceptionSet(operand->gtVNPair));
+ excSetPair = vnStore->VNPUnionExcSet(operand->gtVNPair, excSetPair);
}
- fgValueNumberAddExceptionSetForIndirection(tree, tree->Op(1));
- return;
}
-
- bool encodeResultType = vnEncodesResultTypeForHWIntrinsic(tree->GetHWIntrinsicId());
-
- ValueNumPair excSetPair = ValueNumStore::VNPForEmptyExcSet();
- ValueNumPair normalPair;
- ValueNumPair resvnp = ValueNumPair();
-
- if (encodeResultType)
+ else
{
- ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType());
- resvnp.SetBoth(simdTypeVN);
+ VNFunc func = GetVNFuncForNode(tree);
+ ValueNumPair resultTypeVNPair = ValueNumPair();
+ bool encodeResultType = vnEncodesResultTypeForHWIntrinsic(intrinsicId);
-#ifdef DEBUG
- if (verbose)
+ if (encodeResultType)
{
- printf(" simdTypeVN is ");
- vnPrint(simdTypeVN, 1);
- printf("\n");
+ ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType());
+ resultTypeVNPair.SetBoth(simdTypeVN);
+
+ JITDUMP(" simdTypeVN is ");
+ JITDUMPEXEC(vnPrint(simdTypeVN, 1));
+ JITDUMP("\n");
}
-#endif
- }
- const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(tree->GetHWIntrinsicId()) == -1;
+ auto getOperandVNs = [this, addr](GenTree* operand, ValueNumPair* pNormVNPair, ValueNumPair* pExcVNPair) {
+ vnStore->VNPUnpackExc(operand->gtVNPair, pNormVNPair, pExcVNPair);
- // There are some HWINTRINSICS operations that have zero args, i.e. NI_Vector128_Zero
- if (tree->GetOperandCount() == 0)
- {
- // Currently we don't have intrinsics with variable number of args with a parameter-less option.
- assert(!isVariableNumArgs);
+ // If we have a load operation we will use the fgValueNumberByrefExposedLoad
+ // method to assign a value number that depends upon the current heap state.
+ //
+ if (operand == addr)
+ {
+ // We need to "insert" the "ByrefExposedLoad" VN somewhere here. We choose
+ // to do so by effectively altering the semantics of "addr" operands, making
+ // them represent "the load", on top of which the HWI func itself is applied.
+ // This is a workaround, but doing this "properly" would entail adding the
+ // heap and type VNs to HWI load funcs themselves.
+ var_types loadType = operand->TypeGet();
+ ValueNum loadVN = fgValueNumberByrefExposedLoad(loadType, pNormVNPair->GetLiberal());
- if (encodeResultType)
- {
- // There are zero arg HWINTRINSICS operations that encode the result type, i.e. Vector128_AllBitSet
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, resvnp);
- assert(vnStore->VNFuncArity(func) == 1);
- }
- else
- {
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func);
- assert(vnStore->VNFuncArity(func) == 0);
- }
- }
- else // HWINTRINSIC unary or binary operator.
- {
- ValueNumPair op1vnp;
- ValueNumPair op1Xvnp;
- vnStore->VNPUnpackExc(tree->Op(1)->gtVNPair, &op1vnp, &op1Xvnp);
+ pNormVNPair->SetLiberal(loadVN);
+ pNormVNPair->SetConservative(vnStore->VNForExpr(compCurBB, loadType));
+ }
+ };
- if (tree->GetOperandCount() == 1)
+ const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(intrinsicId) == -1;
+
+ // There are some HWINTRINSICS operations that have zero args, i.e. NI_Vector128_Zero
+ if (tree->GetOperandCount() == 0)
{
- excSetPair = op1Xvnp;
+ // Currently we don't have intrinsics with variable number of args with a parameter-less option.
+ assert(!isVariableNumArgs);
if (encodeResultType)
{
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, resvnp);
- assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs);
+ // There are zero arg HWINTRINSICS operations that encode the result type, i.e. Vector128_AllBitSet
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, resultTypeVNPair);
+ assert(vnStore->VNFuncArity(func) == 1);
}
else
{
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp);
- assert((vnStore->VNFuncArity(func) == 1) || isVariableNumArgs);
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func);
+ assert(vnStore->VNFuncArity(func) == 0);
}
}
- else
+ else // HWINTRINSIC unary or binary operator.
{
- ValueNumPair op2vnp;
- ValueNumPair op2Xvnp;
- vnStore->VNPUnpackExc(tree->Op(2)->gtVNPair, &op2vnp, &op2Xvnp);
+ ValueNumPair op1vnp;
+ ValueNumPair op1Xvnp;
+ getOperandVNs(tree->Op(1), &op1vnp, &op1Xvnp);
- excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
- if (encodeResultType)
+ if (tree->GetOperandCount() == 1)
{
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp, resvnp);
- assert((vnStore->VNFuncArity(func) == 3) || isVariableNumArgs);
+ excSetPair = op1Xvnp;
+
+ if (encodeResultType)
+ {
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, resultTypeVNPair);
+ assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs);
+ }
+ else
+ {
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp);
+ assert((vnStore->VNFuncArity(func) == 1) || isVariableNumArgs);
+ }
}
else
{
- normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp);
- assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs);
+ ValueNumPair op2vnp;
+ ValueNumPair op2Xvnp;
+ getOperandVNs(tree->Op(2), &op2vnp, &op2Xvnp);
+
+ excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
+ if (encodeResultType)
+ {
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp, resultTypeVNPair);
+ assert((vnStore->VNFuncArity(func) == 3) || isVariableNumArgs);
+ }
+ else
+ {
+ normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp);
+ assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs);
+ }
}
}
}
+
tree->gtVNPair = vnStore->VNPWithExc(normalPair, excSetPair);
+
+ // Currently, the only exceptions these intrinsics could throw are NREs.
+ //
+ if (isMemoryLoad || isMemoryStore)
+ {
+ // Most load operations are simple "IND(addr)" equivalents. However, there are exceptions such as AVX
+ // "gather" operations, where the "effective" address - one from which the actual load will be performed and
+ // NullReferenceExceptions are associated with does not match the value of "addr". We will punt handling those
+ // precisely for now.
+ switch (intrinsicId)
+ {
+#ifdef TARGET_XARCH
+ case NI_SSE2_MaskMove:
+ case NI_AVX_MaskStore:
+ case NI_AVX2_MaskStore:
+ case NI_AVX_MaskLoad:
+ case NI_AVX2_MaskLoad:
+ case NI_AVX2_GatherVector128:
+ case NI_AVX2_GatherVector256:
+ case NI_AVX2_GatherMaskVector128:
+ case NI_AVX2_GatherMaskVector256:
+ {
+ ValueNumPair uniqAddrVNPair = vnStore->VNPairForExpr(compCurBB, TYP_BYREF);
+ ValueNumPair uniqExcVNPair = vnStore->VNPairForFunc(TYP_REF, VNF_NullPtrExc, uniqAddrVNPair);
+ ValueNumPair uniqExcSetVNPair = vnStore->VNPExcSetSingleton(uniqExcVNPair);
+
+ tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, uniqExcSetVNPair);
+ }
+ break;
+#endif // TARGET_XARCH
+
+ default:
+ fgValueNumberAddExceptionSetForIndirection(tree, addr);
+ break;
+ }
+ }
}
#endif // FEATURE_HW_INTRINSICS
@@ -9650,19 +9717,14 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
vnpUniq.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
}
-#if defined(FEATURE_READYTORUN) && (defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64))
- if (call->IsR2RRelativeIndir())
+ if (call->GetIndirectionCellArgKind() != WellKnownArg::None)
{
-#ifdef DEBUG
- GenTree* indirectCellAddress = args->GetArgByIndex(0)->GetNode();
- assert(indirectCellAddress->IsCnsIntOrI() && indirectCellAddress->GetRegNum() == REG_R2R_INDIRECT_PARAM);
-#endif // DEBUG
-
- // For ARM indirectCellAddress is consumed by the call itself, so it should have added as an implicit argument
- // in morph. So we do not need to use EntryPointAddrAsArg0, because arg0 is already an entry point addr.
+ // If we are VN'ing a call with indirection cell arg (e.g. because this
+ // is a helper in a R2R compilation) then morph should already have
+ // added this arg, so we do not need to use EntryPointAddrAsArg0
+ // because the indirection cell itself allows us to disambiguate.
useEntryPointAddrAsArg0 = false;
}
-#endif // FEATURE_READYTORUN && (TARGET_ARMARCH || TARGET_LOONGARCH64)
CallArg* curArg = &*args->Args().begin();
if (nArgs == 0)
diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h
index 22755856066a7d..4741e6150debe8 100644
--- a/src/coreclr/jit/valuenum.h
+++ b/src/coreclr/jit/valuenum.h
@@ -379,6 +379,8 @@ class ValueNumStore
// returns true iff vn is known to be a constant int32 that is > 0
bool IsVNPositiveInt32Constant(ValueNum vn);
+ GenTreeFlags GetFoldedArithOpResultHandleFlags(ValueNum vn);
+
public:
// Initializes any static variables of ValueNumStore.
static void InitValueNumStoreStatics();
@@ -685,9 +687,8 @@ class ValueNumStore
op3VN.GetConservative(), op4VN.GetConservative()));
}
- // Get a new, unique value number for an expression that we're not equating to some function,
- // which is the value of a tree in the given block.
- ValueNum VNForExpr(BasicBlock* block, var_types typ = TYP_UNKNOWN);
+ ValueNum VNForExpr(BasicBlock* block, var_types type = TYP_UNKNOWN);
+ ValueNumPair VNPairForExpr(BasicBlock* block, var_types type);
// This controls extra tracing of the "evaluation" of "VNF_MapSelect" functions.
#define FEATURE_VN_TRACE_APPLY_SELECTORS 1
diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp
index b76139fdb604f7..57ce0c09f283df 100644
--- a/src/coreclr/minipal/Unix/doublemapping.cpp
+++ b/src/coreclr/minipal/Unix/doublemapping.cpp
@@ -23,6 +23,18 @@
#if defined(TARGET_OSX) && defined(TARGET_AMD64)
#include
+#include
+
+bool IsProcessTranslated()
+{
+ int ret = 0;
+ size_t size = sizeof(ret);
+ if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)
+ {
+ return false;
+ }
+ return ret == 1;
+}
#endif // TARGET_OSX && TARGET_AMD64
#ifndef TARGET_OSX
@@ -65,6 +77,15 @@ bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecu
*pMaxExecutableCodeSize = MaxDoubleMappedSize;
*pHandle = (void*)(size_t)fd;
#else // !TARGET_OSX
+
+#ifdef TARGET_AMD64
+ if (IsProcessTranslated())
+ {
+ // Rosetta doesn't support double mapping correctly
+ return false;
+ }
+#endif // TARGET_AMD64
+
*pMaxExecutableCodeSize = SIZE_MAX;
*pHandle = NULL;
#endif // !TARGET_OSX
diff --git a/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj b/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj
index 624760868cf2de..a70f6a4f801b6d 100644
--- a/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj
+++ b/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj
@@ -11,6 +11,11 @@
+
+
+
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props
index b150bf6392cb2e..c8ef6487bd504d 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props
@@ -89,7 +89,6 @@ The .NET Foundation licenses this file to you under the MIT license.
-
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props
index d8c01d5a199cf4..e0e595e2f8c98a 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props
@@ -86,6 +86,12 @@ The .NET Foundation licenses this file to you under the MIT license.
+
+
+
+
+
+
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index d5c08a768d970e..9f079b4323fb8c 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -46,7 +46,6 @@ The .NET Foundation licenses this file to you under the MIT license.
- false
true
false
@@ -109,8 +108,7 @@ The .NET Foundation licenses this file to you under the MIT license.
- <_ExcludedPrivateSdkAssemblies Include="$(IlcSdkPath)System.Private.Reflection.Core.dll" Condition="$(IlcDisableReflection) == 'true'" />
-
+
@@ -150,8 +148,7 @@ The .NET Foundation licenses this file to you under the MIT license.
- <_ExcludedPrivateSdkAssemblies Include="$(IlcSdkPath)System.Private.Reflection.Core.dll" Condition="$(IlcDisableReflection) == 'true'" />
-
+
@@ -235,14 +232,14 @@ The .NET Foundation licenses this file to you under the MIT license.
-
+
-
+
diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
index 07dd95f67c980a..6a56d48790efc9 100644
--- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
+++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
@@ -1499,7 +1499,7 @@ public IntPtr Value
// Wrapper around pointers
[StructLayout(LayoutKind.Sequential)]
- internal unsafe readonly struct Pointer where T : unmanaged
+ internal readonly unsafe struct Pointer where T : unmanaged
{
private readonly T* _value;
@@ -1514,7 +1514,7 @@ public T* Value
// Wrapper around pointers that might be indirected through IAT
[StructLayout(LayoutKind.Sequential)]
- internal unsafe readonly struct IatAwarePointer where T : unmanaged
+ internal readonly unsafe struct IatAwarePointer where T : unmanaged
{
private readonly T* _value;
@@ -1546,7 +1546,7 @@ public unsafe IntPtr Value
// Wrapper around relative pointers
[StructLayout(LayoutKind.Sequential)]
- internal unsafe readonly struct RelativePointer where T : unmanaged
+ internal readonly unsafe struct RelativePointer where T : unmanaged
{
private readonly int _value;
@@ -1561,7 +1561,7 @@ public T* Value
// Wrapper around relative pointers that might be indirected through IAT
[StructLayout(LayoutKind.Sequential)]
- internal unsafe readonly struct IatAwareRelativePointer where T : unmanaged
+ internal readonly unsafe struct IatAwareRelativePointer where T : unmanaged
{
private readonly int _value;
diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs
deleted file mode 100644
index f7d6a1d315f848..00000000000000
--- a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-
-namespace System.Runtime.CompilerServices
-{
- //
- // Attach to classes that contain code only used in ILC /BuildType:chk builds.
- //
- // Any class attributed with this must have the following properties:
- //
- // - Class must be declared "static"
- //
- // - All public/internal methods must have a return type of:
- //
- // void
- // bool
- // any non-value type
- //
- // - All fields must be private.
- //
- // - Class constructor must not have externally visible side effects.
- //
- //
- // On /BuildType:ret builds, ILC will run a special transform that
- // turns all of the public and internal method bodies into
- // the equivalent of:
- //
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
- // T Foo()
- // {
- // return default(T);
- // }
- //
- // It also removes all fields and private methods (including the class constructor.)
- //
- // The method semantics must be defined so that ret builds have
- // the desired behavior with these implementations.
- //
- //
- [AttributeUsage(AttributeTargets.Class)]
- internal sealed class DeveloperExperienceModeOnlyAttribute : Attribute
- {
- }
-}
diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs
index f2e0d260b5d12a..78a8de127e59fc 100644
--- a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs
+++ b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs
@@ -5,7 +5,6 @@
namespace System.Runtime.CompilerServices
{
- [DeveloperExperienceModeOnly]
internal static class DeveloperExperienceState
{
public static bool DeveloperExperienceModeEnabled
diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props
index b2cbc3c9bd2b1e..bd21f742967a1e 100644
--- a/src/coreclr/nativeaot/Directory.Build.props
+++ b/src/coreclr/nativeaot/Directory.Build.props
@@ -34,7 +34,7 @@
$(NoWarn);CS8602;CS8603;CS8604;CS8618;CS8625;CS8632;CS8765
- $(NoWarn);CA1810;CA1823;CA1825;CA1852;CA2208;SA1129;SA1205;SA1400;SA1517
+ $(NoWarn);CA1810;CA1823;CA1825;CA1852;CA2208;SA1129;SA1205;SA1400;SA1517;IDE0065
$(NoWarn);CS3016
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
index 3b081e5d64cfcf..b277d80d79784b 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
@@ -303,7 +303,7 @@ public static unsafe int RhGetCurrentThreadStackTrace(IntPtr[] outputBuffer)
// Use DllImport here instead of LibraryImport because this file is used by Test.CoreLib.
[DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- private static unsafe extern int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame);
+ private static extern unsafe int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame);
// Worker for RhGetCurrentThreadStackTrace. RhGetCurrentThreadStackTrace just allocates a transition
// frame that will be used to seed the stack trace and this method does all the real work.
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs
index 76234dde14e4d7..62c26ccd305efd 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs
@@ -217,7 +217,7 @@ public unsafe IntPtr AllocateThunk()
int thunkIndex = (int)(((nuint)(nint)nextAvailableThunkPtr) - ((nuint)(nint)nextAvailableThunkPtr & ~Constants.PageSizeMask));
Debug.Assert((thunkIndex % Constants.ThunkDataSize) == 0);
- thunkIndex = thunkIndex / Constants.ThunkDataSize;
+ thunkIndex /= Constants.ThunkDataSize;
IntPtr thunkAddress = InternalCalls.RhpGetThunkStubsBlockAddress(nextAvailableThunkPtr) + thunkIndex * Constants.ThunkCodeSize;
diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt
index 8ce6ea0d296722..3ad3ceba7c8f0e 100644
--- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt
+++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt
@@ -245,10 +245,7 @@ add_definitions(-D_LIB)
if(WIN32)
add_definitions(-DFEATURE_ETW)
add_definitions(-DFEATURE_EVENT_TRACE)
- # redirection on ARM64 should work, but needs to be tested
- if (CLR_CMAKE_TARGET_ARCH_AMD64)
- add_definitions(-DFEATURE_SUSPEND_REDIRECTION)
- endif()
+ add_definitions(-DFEATURE_SUSPEND_REDIRECTION)
else()
add_definitions(-DNO_UI_ASSERT)
include(unix/configure.cmake)
diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp
index e65889cd3fc544..2d8d3b2b2baa29 100644
--- a/src/coreclr/nativeaot/Runtime/startup.cpp
+++ b/src/coreclr/nativeaot/Runtime/startup.cpp
@@ -60,6 +60,30 @@ int g_cpuFeatures = 0;
EXTERN_C int g_requiredCpuFeatures;
#endif
+#ifdef TARGET_UNIX
+static bool InitGSCookie();
+
+//-----------------------------------------------------------------------------
+// GSCookies (guard-stack cookies) for detecting buffer overruns
+//-----------------------------------------------------------------------------
+
+#ifdef __APPLE__
+#define READONLY_ATTR_ARGS section("__DATA,__const")
+#else
+#define READONLY_ATTR_ARGS section(".rodata")
+#endif
+#define READONLY_ATTR __attribute__((READONLY_ATTR_ARGS))
+
+// Guard-stack cookie for preventing against stack buffer overruns
+typedef size_t GSCookie;
+
+// const is so that it gets placed in the .text section (which is read-only)
+// volatile is so that accesses to it do not get optimized away because of the const
+//
+
+extern "C" volatile READONLY_ATTR const GSCookie __security_cookie = 0;
+#endif // TARGET_UNIX
+
static bool InitDLL(HANDLE hPalInstance)
{
#ifdef FEATURE_CACHED_INTERFACE_DISPATCH
@@ -119,6 +143,11 @@ static bool InitDLL(HANDLE hPalInstance)
return false;
#endif
+#ifdef TARGET_UNIX
+ if (!InitGSCookie())
+ return false;
+#endif
+
if (!g_CastCacheLock.InitNoThrow(CrstType::CrstCastCache))
return false;
@@ -275,6 +304,34 @@ bool DetectCPUFeatures()
}
#endif // !USE_PORTABLE_HELPERS
+#ifdef TARGET_UNIX
+inline
+GSCookie * GetProcessGSCookiePtr() { return const_cast(&__security_cookie); }
+
+bool InitGSCookie()
+{
+ volatile GSCookie * pGSCookiePtr = GetProcessGSCookiePtr();
+
+ // The GS cookie is stored in a read only data segment
+ if (!PalVirtualProtect((void*)pGSCookiePtr, sizeof(GSCookie), PAGE_READWRITE))
+ {
+ return false;
+ }
+
+ // REVIEW: Need something better for PAL...
+ GSCookie val = (GSCookie)PalGetTickCount64();
+
+#ifdef _DEBUG
+ // In _DEBUG, always use the same value to make it easier to search for the cookie
+ val = (GSCookie)(0x9ABCDEF012345678);
+#endif
+
+ *pGSCookiePtr = val;
+
+ return PalVirtualProtect((void*)pGSCookiePtr, sizeof(GSCookie), PAGE_READONLY);
+}
+#endif // TARGET_UNIX
+
#ifdef PROFILE_STARTUP
#define STD_OUTPUT_HANDLE ((uint32_t)-11)
diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
index df0dc0a20339c7..aa5cfeb3cde21d 100644
--- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
+++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
@@ -734,6 +734,9 @@ static int W32toUnixAccessControl(uint32_t flProtect)
case PAGE_EXECUTE_READWRITE:
prot = PROT_READ | PROT_WRITE | PROT_EXEC;
break;
+ case PAGE_READONLY:
+ prot = PROT_READ;
+ break;
default:
ASSERT(false);
break;
@@ -812,7 +815,11 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddre
{
int unixProtect = W32toUnixAccessControl(protect);
- return mprotect(pAddress, size, unixProtect) == 0;
+ // mprotect expects the address to be page-aligned
+ uint8_t* pPageStart = ALIGN_DOWN((uint8_t*)pAddress, OS_PAGE_SIZE);
+ size_t memSize = ALIGN_UP((uint8_t*)pAddress + size, OS_PAGE_SIZE) - pPageStart;
+
+ return mprotect(pPageStart, memSize, unixProtect) == 0;
}
REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer)
diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
index 4010dbea5241fe..7738a7455c757c 100644
--- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
+++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
@@ -693,7 +693,6 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
PTR_PTR_VOID * ppvRetAddrLocation, // out
GCRefKind * pRetValueKind) // out
{
-#if defined(TARGET_AMD64)
CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
size_t unwindDataBlobSize;
@@ -719,7 +718,11 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
p += sizeof(int32_t);
// Decode the GC info for the current method to determine its return type
- GcInfoDecoder decoder(GCInfoToken(p), DECODE_RETURN_KIND);
+ GcInfoDecoderFlags flags = DECODE_RETURN_KIND;
+#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+ flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS);
+#endif // TARGET_ARM || TARGET_ARM64
+ GcInfoDecoder decoder(GCInfoToken(p), flags);
GCRefKind gcRefKind = GetGcRefKind(decoder.GetReturnKind());
@@ -728,6 +731,11 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
SIZE_T EstablisherFrame;
PVOID HandlerData;
CONTEXT context;
+#ifdef _DEBUG
+ memset(&context, 0xDD, sizeof(context));
+#endif
+
+#if defined(TARGET_AMD64)
context.Rsp = pRegisterSet->GetSP();
context.Rbp = pRegisterSet->GetFP();
context.Rip = pRegisterSet->GetIP();
@@ -744,6 +752,55 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
*ppvRetAddrLocation = (PTR_PTR_VOID)(context.Rsp - sizeof (PVOID));
*pRetValueKind = gcRefKind;
return true;
+#elif defined(TARGET_ARM64)
+
+ if (decoder.HasTailCalls())
+ {
+ // Do not hijack functions that have tail calls, since there are two problems:
+ // 1. When a function that tail calls another one is hijacked, the LR may be
+ // stored at a different location in the stack frame of the tail call target.
+ // So just by performing tail call, the hijacked location becomes invalid and
+ // unhijacking would corrupt stack by writing to that location.
+ // 2. There is a small window after the caller pops LR from the stack in its
+ // epilog and before the tail called function pushes LR in its prolog when
+ // the hijacked return address would not be not on the stack and so we would
+ // not be able to unhijack.
+ return false;
+ }
+
+ context.Sp = pRegisterSet->GetSP();
+ context.Fp = pRegisterSet->GetFP();
+ context.Pc = pRegisterSet->GetIP();
+ context.Lr = *pRegisterSet->pLR;
+
+ KNONVOLATILE_CONTEXT_POINTERS contextPointers;
+#ifdef _DEBUG
+ memset(&contextPointers, 0xDD, sizeof(contextPointers));
+#endif
+ contextPointers.Lr = pRegisterSet->pLR;
+
+ RtlVirtualUnwind(NULL,
+ dac_cast(m_moduleBase),
+ pRegisterSet->IP,
+ (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction,
+ &context,
+ &HandlerData,
+ &EstablisherFrame,
+ &contextPointers);
+
+ if (contextPointers.Lr == pRegisterSet->pLR)
+ {
+ // This is the case when we are either:
+ //
+ // 1) In a leaf method that does not push LR on stack, OR
+ // 2) In the prolog/epilog of a non-leaf method that has not yet pushed LR on stack
+ // or has LR already popped off.
+ return false;
+ }
+
+ *ppvRetAddrLocation = (PTR_PTR_VOID)contextPointers.Lr;
+ *pRetValueKind = gcRefKind;
+ return true;
#else
return false;
#endif // defined(TARGET_AMD64)
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml
index 3aeeaf70faefd9..48edfc4f2540c2 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml
@@ -17,4 +17,7 @@
+
+
+
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
index 0499890c36ac62..a6d77cbedf7cdf 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs
@@ -100,7 +100,7 @@ public virtual void TryGetMethodBase(IntPtr methodStartAddress, out MethodBase m
public virtual bool OnContractFailure(string? stackTrace, ContractFailureKind contractFailureKind, string? displayMessage, string userMessage, string conditionText, Exception innerException)
{
- Debug.WriteLine("Assertion failed: " + (displayMessage == null ? "" : displayMessage));
+ Debug.WriteLine("Assertion failed: " + (displayMessage ?? ""));
if (Debugger.IsAttached)
Debugger.Break();
return false;
diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
similarity index 92%
rename from src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs
rename to src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
index 0a7bab62ea3620..f68d8407b2781b 100644
--- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
@@ -6,12 +6,15 @@
using System.Reflection;
using Internal.Metadata.NativeFormat;
using System.Reflection.Runtime.General;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Internal.Reflection.Core
{
// Auto StructLayout used to suppress warning that order of fields is not guaranteed in partial structs
[StructLayout(LayoutKind.Auto)]
+ [ReflectionBlocked]
+ [CLSCompliant(false)]
public partial struct AssemblyBindResult
{
public MetadataReader Reader;
@@ -25,6 +28,8 @@ public partial struct AssemblyBindResult
//
// If the binder cannot locate an assembly, it must return null and set "exception" to an exception object.
//
+ [ReflectionBlocked]
+ [CLSCompliant(false)]
public abstract class AssemblyBinder
{
public const string DefaultAssemblyNameForGetType = "System.Private.CoreLib";
diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
similarity index 97%
rename from src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
rename to src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
index f2065074d78b3f..02f6a206015db2 100644
--- a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
@@ -16,6 +16,7 @@
#endif
using System.Reflection.Runtime.TypeParsing;
using System.Reflection.Runtime.CustomAttributes;
+using System.Runtime.CompilerServices;
using Internal.Metadata.NativeFormat;
using Internal.Runtime.Augments;
@@ -24,6 +25,8 @@ namespace Internal.Reflection.Core.Execution
//
// This singleton class acts as an entrypoint from System.Private.Reflection.Execution to System.Private.Reflection.Core.
//
+ [ReflectionBlocked]
+ [CLSCompliant(false)]
public sealed class ExecutionDomain
{
internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEnvironment executionEnvironment)
@@ -124,7 +127,7 @@ private static CoreTypeResolver CreateCoreTypeResolver(Func
-
@@ -344,9 +343,15 @@
+
+ ArrayBuilder.cs
+
Utilities\LockFreeReaderHashtable.cs
+
+ System\Collections\Generic\EnumerableExtensions.cs
+
System\Collections\Generic\LowLevelList.cs
@@ -374,6 +379,9 @@
System\Runtime\CompilerServices\__BlockReflectionAttribute.cs
+
+ System\Runtime\CompilerServices\DeveloperExperienceState.cs
+
Internal\Runtime\CanonTypeKind.cs
@@ -398,6 +406,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
true
diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ActivatorImplementation.cs
similarity index 94%
rename from src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs
rename to src/coreclr/nativeaot/System.Private.CoreLib/src/System/ActivatorImplementation.cs
index 4370581bd7c6e2..8e3aef57b8ca5c 100644
--- a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ActivatorImplementation.cs
@@ -28,7 +28,7 @@ public static object CreateInstance(
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
if (nonPublic)
bindingFlags |= BindingFlags.NonPublic;
- ConstructorInfo constructor = type.GetConstructor(bindingFlags, null, CallingConventions.Any, Array.Empty(), null);
+ ConstructorInfo? constructor = type.GetConstructor(bindingFlags, null, CallingConventions.Any, Array.Empty(), null);
if (constructor == null)
{
if (type.IsValueType)
@@ -44,7 +44,7 @@ public static object CreateInstance(
[DebuggerGuidedStepThrough]
public static object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
- Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes)
+ Type type, BindingFlags bindingAttr, Binder binder, object?[]? args, CultureInfo? culture, object?[]? activationAttributes)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
@@ -64,7 +64,7 @@ public static object CreateInstance(
args = Array.Empty