diff --git a/.editorconfig b/.editorconfig
index 17dafd92468ea3..15658bab8d06ef 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,9 +12,6 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
-[project.json]
-indent_size = 2
-
# Generated code
[*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}]
generated_code = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 34af0791cdd998..1c41d9d4e873d6 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -10,8 +10,12 @@
/src/coreclr/inc/corinfo.h @dotnet/jit-contrib
/src/coreclr/inc/corjit.h @dotnet/jit-contrib
/src/coreclr/jit/ @dotnet/jit-contrib
+/src/coreclr/nativeaot @MichalStrehovsky
/src/coreclr/tools/Common @dotnet/crossgen-contrib @MichalStrehovsky
/src/coreclr/tools/aot @dotnet/crossgen-contrib
+/src/coreclr/tools/aot/ILCompiler.Compiler @MichalStrehovsky
+/src/coreclr/tools/aot/ILCompiler.RyuJit @MichalStrehovsky
+/src/coreclr/tools/aot/ILCompiler.MetadataTransform @MichalStrehovsky
# Mono Code Owners
diff --git a/docs/design/coreclr/botr/ilc-architecture.md b/docs/design/coreclr/botr/ilc-architecture.md
new file mode 100644
index 00000000000000..697f2b7a852e9e
--- /dev/null
+++ b/docs/design/coreclr/botr/ilc-architecture.md
@@ -0,0 +1,191 @@
+# ILC Compiler Architecture
+
+Author: Michal Strehovsky ([@MichalStrehovsky](https://github.com/MichalStrehovsky)) - 2018
+
+ILC (IL Compiler) is an ahead of time compiler that transforms programs in CIL (Common Intermediate Language) into a target language or instruction set to be executed on a stripped down CoreCLR runtime. The input to ILC is the common instruction format generated by popular managed language compilers such as C#, VB.NET, or F#. The output of ILC is native code for the target platform, along with data structures required to support executing the code on the target runtime. With a bit of stretch, one could say that ILC is an ahead of time native compiler for C#.
+
+Traditionally, CIL has been compiled "just in time" (JIT). What this means is that the translation from CIL to the instruction set executable on the target runtime environment happened on an as-needed basis when the native code became necessary to continue execution of the program (e.g. on a first call to a CIL method). An ahead of time compiler tries to prepare the code and data structures in advance - before the program starts executing. The major advantages of having native code and data structures required for the code to execute available in advance are significant improvements to program startup time and working set.
+
+In a fully ahead of time compiled environment, the compiler is responsible for generating code and data structures for everything that might be needed at runtime - the presence of the original CIL instructions or program metadata (names of methods and their signature, for example) is no longer necessary after compilation. One important aspect to keep in mind is that ahead of time compilation does not preclude just in time compilation: one could imagine mixed modes of execution where some parts of the application are compiled ahead of time, while others are compiled just in time, or interpreted. ILC needs to support such modes of operations, since both have their advantages and disadvantages. We have prototyped such modes of execution in the past.
+
+## Goals
+
+* Compile CIL and produce native code for the target platform
+* Generate essential data structures that the runtime requires to execute managed native code (exception handling and GC information for methods, data structures describing types, their GC layout and vtables, interface dispatch maps, etc.)
+* Generate optional data structures the base class libraries require to provide rich managed APIs to user code (data structures that support reflection, interop, textual stack trace information, type loading at runtime, etc.)
+* Support optional inputs from a whole program analysis step to influence compilation
+* Support generating executable files and static/dynamic libraries (with a flat C-style public API surface)
+* Support multiple modes of compilation:
+ * Single-file output:
+ * All input assemblies merged into a single object file generated by ILC (merging managed assemblies happens in ILC). This mode allows maximum optimization.
+ * Generating multiple object files that are merged using a platform linker into a single executable (merging happens in the native linker after ILC runs). This mode allows incremental compilation at the cost of inhibiting optimizations in ILC.
+ * Multi-file output (one or more input assemblies generating one or more dynamic libraries that link against each other dynamically). This mode allows code/data sharing among several executables or dynamic libraries, but inhibits many optimizations.
+* Multi-threaded compilation
+* Generate native debug information for the target platform to allow debugging with native debuggers
+* Generate outputs in the platform's native object file format (`.obj` and `.o` files)
+* Have a defined behavior when input is incomplete (e.g. assemblies missing, or an assembly version mismatch)
+
+## ILC composition
+ILC is composed of roughly 3 parts: the compilation driver, the compiler, and the code generation backends.
+
+### Compilation driver
+The role of the compilation driver is to parse command line arguments, set up the compiler, and run the compilation. The process of setting up the compiler involves configuring a `CompilationBuilder`. The compilation builder exposes methods that let the driver configure and compose various pieces of the compiler as directed by the command line arguments. These components influence what gets compiled and how the compilation happens. Eventually the driver constructs a `Compilation` object that provides methods to run the compilation, inspect the results of the compilation, and write the outputs to a file on disk.
+
+Related classes: `CompilationBuilder`, `ICompilation`
+
+### Compiler
+Compiler is the core component that the rest of the document is going to talk about. It's responsible for running the compilation process and generating data structures for the target runtime and target base class libraries.
+
+The compiler tries to stay policy free as to what to compile, what data structures to generate, and how to do the compilation. The specific policies are supplied by the compilation driver as part of configuring the compilation.
+
+### Code generation backends
+ILC is designed to support multiple code generation backends that target the same runtime. What this means is that we have a model where there are common parts within the compiler (determining what needs to be compiled, generating data structures for the underlying runtime), and parts that are specific for a target environment. The common parts (the data structure layout) are not target specific - the target specific differences are limited to general questions, such as "does the target have representation for relative pointers?", but the basic shape of the data structures is the same, no matter the target platform.
+ILC currently supports following codegen backends (with varying levels of completeness):
+
+* **RyuJIT**: native code generator also used as the JIT compiler in CoreCLR. This backend supports x64, arm64, arm32 on Windows, Linux, macOS, and BSD.
+* **LLVM**: the LLVM backend is currently used to generate WebAssembly code in connection with Emscripten. Lives in [NativeAOT-LLVM branch](https://github.com/dotnet/runtimelab/tree/feature/NativeAOT-LLVM).
+
+This document describes the common parts of the compiler that are applicable to all codegen backends.
+
+In the past, ILC supported following backends:
+
+* **CppCodegen**: a portable code generator that translates CIL into C++ code. This supports rapid platform bringup (instead of having to build a code generator for a new CPU architecture, it relies on the C++ compiler the platform likely already has). Portability comes at certain costs. This codegen backend wasn't [brought over](https://github.com/dotnet/corert/tree/master/src/ILCompiler.CppCodeGen/src) from the now archived CoreRT repo.
+
+Related project files: ILCompiler.LLVM.csproj, ILCompiler.RyuJit.csproj
+
+## Dependency analysis
+The core concept driving the compilation in ILC is dependency analysis. Dependency analysis is the process of determining the set of runtime artifacts (method code bodies and various data structures) that need to be generated into the output object file. Dependency analysis builds a graph where each vertex either
+* represents an artifact that will be part of the output file (such as "compiled method body" or "data structure describing a type at runtime") - this is an "object node", or
+* captures certain abstract characteristic of the compiled program (such as "the program contains a virtual call to the `Object.GetHashCode` method") - a general "dependency node". General dependency nodes do not physically manifest themselves as bytes in the output, but they usually have edges that (transitively) lead to object nodes that do form parts of the output.
+
+The edges of the graph represent a "requires" relationship. The compilation process corresponds to building this graph and determining what nodes are part of the graph.
+
+Related classes: `DependencyNodeCore<>`, `ObjectNode`
+
+Related project files: ILCompiler.DependencyAnalysisFramework.csproj
+
+### Dependency expansion process
+The compilation starts with a set of nodes in the dependency graph called compilation roots. The roots are specified by the compilation driver and typically contain the `Main()` method, but the exact set of roots depends on the compilation mode: the set of roots will be different when we're e.g. building a library, or when we're doing a multi-file compilation, or when we're building a single file app.
+
+The process begins by looking at the list of the root nodes and establishing their dependencies (dependent nodes). Once the dependencies are known, the compilation moves on to inspecting the dependencies of the dependencies, and so on, until all dependencies are known and marked within the dependency graph. When that happens, the compilation is done.
+
+The expansion of the graph is required to stay within the limits of a compilation group. Compilation group is a component that controls how the dependency graph is expanded. The role of it is best illustrated by contrasting a multifile and single file compilation: in a single file compilation, all methods and types that are statically reachable from the roots become part of the dependency graph, irrespective of the input assembly that defines them. In a multi-file compilation, some of the compilation happens as part of a different unit of work: the methods and types that are not part of the current unit of work shouldn't have their dependencies examined and they should not be a part of the dependency graph.
+
+The advantage of having the two abstractions (compilation roots, and a class that controls how the dependency graph is expanded) is that the core compilation process can be completely unaware of the specific compilation mode (e.g. whether we're building a library, or whether we're doing a multi-file compilation). The details are fully wrapped behind the two abstractions and give us a great expressive power for defining or experimenting with new compilation modes, while keeping all the logic in a single place. For example, we support a single method compilation mode where we compile only one method. This mode is useful for troubleshooting code generation. The compilation driver can define additional compilation modes (e.g. a mode that compiles a single type and all the associated methods) without having to change the compiler itself.
+
+Related classes: `ICompilationRootProvider`, `CompilationModuleGroup`
+
+### Dependency types
+The dependency graph analysis can work with several kinds of dependencies between the nodes:
+* **Static dependencies**: these are the most common. If node A is part of the dependency graph and it declares it requires node B, node B also becomes part of the dependency graph.
+* **Conditional dependencies**: Node A declares that it depends on node B, but only if node C is part of the graph. If that's the case, node B will become part of the graph if both A and C are in the graph.
+* **Dynamic dependencies**: These are quite expensive to have in the system, so we only use them rarely. They let the node inspect other nodes within the graph and inject nodes based on their presence. They are pretty much only used for analysis of generic virtual methods.
+
+To show how the dependency graph looks like in real life let's look at an example of how an (optional) optimization within the compiler around virtual method usage tracking works:
+
+```csharp
+abstract class Foo
+{
+ public abstract void VirtualMethod();
+ public virtual void UnusedVirtualMethod() { }
+}
+
+class Bar : Foo
+{
+ public override void VirtualMethod() { }
+ public override void UnusedVirtualMethod() { }
+}
+
+class Baz : Foo
+{
+ public override void VirtualMethod() { }
+}
+
+class Program
+{
+ static int Main()
+ {
+ Foo f = new Bar();
+ f.VirtualMethod();
+ return f is Baz ? 0 : 100;
+ }
+}
+```
+
+The dependency graph for the above program would look something like this:
+
+
+
+The rectangle-shaped nodes represent method bodies, the oval-shaped nodes represent types, the dashed rectangles represent virtual method use, and the dotted oval-shaped node is an unconstructed type.
+The dashed edges are conditional dependencies, with the condition marked on the label.
+
+* `Program::Main` creates a new instance of `Bar`. For that, it will allocate an object on the GC heap and call a constructor to initialize it. Therefore, it needs the data structure that represents the `Bar` type and `Bar`'s default constructor. The method then calls `VirtualMethod`. Even though from this simple example we know what specific method body this will end up calling (we can devirtualize the call in our head), we can't know in general, so we say `Program::Main` also depends on "Virtual method use of Foo::VirtualMethod". The last line of the program performs a type check. To do the type check, the generated code needs to reference a data structure that represents type `Baz`. The interesting thing about a type check is that we don't need to generate a full data structure describing the type, only enough to be able to tell if the cast succeeds. So we say `Program::Main` also depends on "unconstructed type data structure" for `Baz`.
+* The data structure that represents type `Bar` has two important kinds of dependencies. It depends on its base type (`Foo`) - a pointer to it is required to make casting work - and it also contains the vtable. The entries in the vtable are conditional - if a virtual method is never called, we don't need to place it in the vtable. As a result of the situation in the graph, the method body for `Bar::VirtualMethod` is going to be part of the graph, but `Bar::UnusedVirtualMethod` will not, because it's conditioned on a node that is not present in the graph.
+* The data structure that represents `Baz` is a bit different from `Bar`. We call this an "unconstructed type" structure. Unconstructed type structures don't contain a vtable, and therefore `Baz` is missing a virtual method use dependency for `Baz::VirtualMethod` conditioned on the use of `Foo::VirtualMethod`.
+
+Notice how using conditional dependencies helped us avoid compiling method bodies for `Foo::UnusedVirtualMethod` and `Bar::UnusedVirtualMethod` because the virtual method is never used. We also avoided generating `Baz::VirtualMethod`, because `Baz` was never allocated within the program. We generated the data structure that represents `Baz`, but because the data structure was only generated for the purposes of casting, it doesn't have a vtable that would pull `Baz::VirtualMethod` into the dependency graph.
+
+Note that while "constructed" and "unconstructed" type nodes are modelled separately in the dependency graph, at the object writing time they get coalesced into one. If the graph has a type in both the unconstructed and constructed form, only the constructed form will be emitted into the executable and places referring to the unconstructed form will be redirected to the constructed form, to maintain type identity.
+
+Related compiler switches: `--dgmllog` serializes the dependency graph into an XML file. The XML file captures all the nodes in the graph, but only captures the first edge leading to the node (knowing the first edge is enough for most purposes). `--fulllog` generates an even bigger XML file that captures all the edges.
+
+Related tools: [Dependency analysis viewer](../how-to-debug-compiler-dependency-analysis.md) is a tool that listens to ETW events generated by all the ILC compiler processes on the machine and lets you interactively explore the graph.
+
+## Object writing
+The final phase of compilation is writing out the outputs. The output of the compilation depends on the target environment but will typically be some sort of object file. An object file typically consists of blobs of code or data with links (or relocations) between them, and symbols: named locations within a blob. The relocations point to symbols, either defined within the same object file, or in a different module.
+
+While the object file format is highly target specific, the compiler represents dependency nodes that have object data associated with them the same way irrespective of the target - with the `ObjectNode` class. `ObjectNode` class allows its children to specify the section where to place their data (code, read only data, uninitialized data, etc.), and crucially, the data itself (represented by the `ObjectData` class returned from `GetObjectData` method).
+
+On a high level, the role of the object writer is to go over all the marked `ObjectNode`s in the graph, retrieve their data, defined symbols, and relocations to other symbols, and store them in the object file.
+
+NativeAOT compiler contains multiple object writers:
+* Native object writer (`src/coreclr/tools/aot/ObjWriter`) based on LLVM that is capable of producing Windows PE, Linux ELF, and macOS Mach-O file formats
+* Native object writer based on LLVM for WebAssembly
+* Ready to run object writer that generates mixed CIL/native executables in the ready to run format for CoreCLR
+
+Related command line arguments: `--map` produces a map of all the object nodes that were emitted into the object file.
+
+## Optimization pluggability
+An advantage of a fully ahead of time compiled environment is that the compiler can make closed world assumptions about the code being compiled. For example: lacking the ability to load arbitrary CIL at runtime (either through `Assembly.Load`, or `Reflection.Emit`), if the compiler sees that there's only one type implementing an interface, it can replace all the interface calls in the program with direct calls, and apply additional optimizations enabled by it, such as inlining. If the target environment allowed dynamic code, such optimization would be invalid.
+
+The compiler is structured to allow such optimizations, but remains policy-free as to when the optimization should be applied. This allow both fully AOT compiled and mixed (JIT/interpreted) code execution strategies. The policies are always captured in an abstract class or an interface, the implementation of which is selected by the compilation driver and passed to the compilation builder. This allows a great degree of flexibility and gives a lot of power to influence the compilation from the compilation driver, without hardcoding the conditions when the optimization is applicable into the compiler.
+
+An example of such policy is the virtual method table (vtable) generation policy. The compiler can build vtables two ways: lazily, or by reading the type's metadata and generating a vtable slot for every new virtual method present in the type's method list. The depenceny analysis example graph a couple sections above was describing how conditional dependencies can be used to track what vtable slots and virtual method bodies we need to generate for a program to work. This is an example of an optimization that requires closed world assumptions. The policy is captured in a `VTableSliceProvider` class and allows the driver to select the vtable generation policy per type. This allows the compilation driver a great degree of control to fine tune when the optimization is allowed to happen (e.g. even in the presence of a JIT, we could still allow this optimization to happen on types that are not visible/accessible from the non-AOT compiled parts of the program or through reflection).
+
+The policies that can be configured in the driver span a wide range of areas: generation of reflection metadata, devirtualization, generation of vtables, generation of stack trace metadata for `Exception.ToString`, generation of debug information, the source of IL for method bodies, etc.
+
+## IL scanning
+
+Another component of ILC is the IL scanner. IL scanning is an optional step that can be executed before the compilation. In many ways, the IL scanning acts as another compilation with a null/dummy code generation backend. The IL scanner scans the IL of all the method bodies that become part of the dependency graph starting from the roots and expands their dependencies. The IL scanner ends up building the same dependency graph a code generation backend would, but the nodes in the graph that represent method bodies don't have any machine code instructions associated with them. This process is relatively fast since there's no code generation involved, but the resulting graph contains a lot of valuable insights into the compiled program. The dependency graph built by the IL scanner is a strict superset of the graph built by a real compilation since the IL scanner doesn't model optimizations such as inlining and devirtualization.
+
+The results of the IL scanner are input into the subsequent compilation process. For example, the IL scanner can use the lazy vtable generation policy to build vtables with just the slots needed, and assign slot numbers to each slot in the vtable at the end of scanning. The vtable layouts computed lazily during scanning can then be used by the real compilation process to inline vtable lookups at the callsites. Inlining the vtable lookup at the callsite would not be possible with a lazy vtable generation policy because the exact slot assignments of lazy vtables aren't stable until the compilation is done.
+
+The IL scanning process is optional and the compilation driver can skip it if compilation throughput is more important than runtime code quality.
+
+Related classes: `ILScanner`
+
+## Coupling with the base class libraries
+The compiler has a certain level of coupling with the underlying base class library (the `System.Private.*` libraries within the repo). The coupling is twofold:
+* Binary format of the generated data structures
+* Expectations about the existence of certain methods within the core library
+
+Examples of the binary formats generated by the compiler and used by the base class libraries would be the format of the data structure that represents a type at runtime (`MethodTable`), or the blob of bytes that describes non-essential information about the type (such as the type name, or a list of methods). These data structures form a contract and allow the managed code in the base class library to provide rich services to user code through library APIs at runtime (such as the reflection APIs). Generation of some of these data structures is optional, but for some it's mandatory because they're required to execute any managed code.
+
+The compiler also needs to call into some well-known entrypoints within the base class library to support the generated code. The base class library needs to define these methods. Examples of such entrypoints would be various helpers to throw `OverflowException` during mathematical operations, `IndexOutOfRangeException` during array access, or various helpers to aid in generating p/invoke marshalling code (e.g. converting a UTF-16 string to ANSI and back before/after invoking the native method).
+
+One interesting thing to point out is that the coupling of the compiler with the base class libraries is relatively loose (there are only few mandatory parts). This allows different base class libraries to be used with ILC. Such base class libraries could look quite different from what regular .NET developers are used to (e.g. a `System.Object` that doesn't have a `ToString` method) but could allow using type safe code in environments where regular .NET would be considered "too heavy". Various experiments with such lightweight code have been done in the past, and some of them even shipped as part of the Windows operating system.
+
+Example of such alternative base class library is [Test.CoreLib](../../../../src/coreclr/nativeaot/Test.CoreLib/). The `Test.CoreLib` library provides a very minimal API surface. This, coupled with the fact that it requires almost no initialization, makes it a great assistant in bringing NativeAOT to new platforms.
+
+## Compiler-generated method bodies
+Besides compiling the code provided by the user in the form of input assemblies, the compiler also needs to compile various helpers generated within the compiler. The helpers are used to lower some of the higher-level .NET constructs into concepts that the underlying code generation backend understands. These helpers are emitted as IL code on the fly, without being physically backed by IL in an assembly on disk. Having the higher level concepts expressed as regular IL helps avoid having to implement the higher-level concept in each code generation backend (we only must do it once because IL is the thing all backends understand).
+
+The helpers serve various purposes such as:
+* Helpers to support invoking delegates
+* Helpers that support marshalling parameters and return values for P/Invoke
+* Helpers that support `ValueType.GetHashCode` and `ValueType.Equals`
+* Helpers that support reflection: e.g. `Assembly.GetExecutingAssembly`
+
+Related classes: `ILEmitter`, `ILStubMethod`
+
+Related ILC command line switches: `--ildump` to dump all generated IL into a file and map debug information to it (allows source stepping through the generated IL at runtime).
diff --git a/docs/design/coreclr/botr/images/simple-dependency-graph.gv b/docs/design/coreclr/botr/images/simple-dependency-graph.gv
new file mode 100644
index 00000000000000..48fe5e44e4460c
--- /dev/null
+++ b/docs/design/coreclr/botr/images/simple-dependency-graph.gv
@@ -0,0 +1,43 @@
+digraph "SimpleDependencyGraph" {
+
+ordering=out;
+rankdir=LR;
+
+node [shape=box];
+Code_Program_Main[label="Program::Main"];
+Code_Bar__ctor[label="Bar::.ctor"];
+Code_Bar_VirtualMethod[label="Bar::VirtualMethod"];
+Code_Bar_UnusedVirtualMethod[label="Bar::UnusedVirtualMethod"];
+Code_Foo_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod"];
+Code_Foo__ctor[label="Foo::.ctor"];
+Code_Object__ctor[label="Object::.ctor"];
+
+node [shape=ellipse];
+Type_Bar[label="Bar"];
+Type_Foo[label="Foo"];
+Type_Object[label="Object"];
+
+node [shape=ellipse, style=dotted]
+Type_Baz[label="Baz"]
+
+node [shape=box, style=dashed];
+Virtual_Foo_VirtualMethod[label="Foo::VirtualMethod"];
+
+Code_Program_Main -> Code_Bar__ctor;
+Code_Program_Main -> Type_Bar;
+Code_Program_Main -> Virtual_Foo_VirtualMethod;
+Code_Program_Main -> Type_Baz;
+Type_Baz -> Type_Foo;
+
+Type_Bar -> Type_Foo;
+Type_Foo -> Type_Object;
+Type_Bar -> Code_Bar_VirtualMethod[label="Foo::VirtualMethod is used", style=dashed];
+Type_Bar -> Code_Bar_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod is used", style=dashed];
+Type_Foo -> Code_Foo_UnusedVirtualMethod[label="Foo::UnusedVirtualMethod is used", style=dashed];
+Code_Bar__ctor -> Code_Foo__ctor;
+Code_Foo__ctor -> Code_Object__ctor;
+
+overlap=false;
+fontsize=12;
+
+}
diff --git a/docs/design/coreclr/botr/images/simple-dependency-graph.svg b/docs/design/coreclr/botr/images/simple-dependency-graph.svg
new file mode 100644
index 00000000000000..60ca83174222ae
--- /dev/null
+++ b/docs/design/coreclr/botr/images/simple-dependency-graph.svg
@@ -0,0 +1,136 @@
+
+
+
+
+
diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md
index bcbe91245f84bd..857282f30299c6 100644
--- a/docs/design/coreclr/botr/readytorun-format.md
+++ b/docs/design/coreclr/botr/readytorun-format.md
@@ -719,7 +719,6 @@ enum ReadyToRunHelper
READYTORUN_HELPER_Unbox = 0x5A,
READYTORUN_HELPER_Unbox_Nullable = 0x5B,
READYTORUN_HELPER_NewMultiDimArr = 0x5C,
- READYTORUN_HELPER_NewMultiDimArr_NonVarArg = 0x5D,
// Helpers used with generic handle lookup cases
READYTORUN_HELPER_NewObject = 0x60,
diff --git a/docs/workflow/building/coreclr/nativeaot.md b/docs/workflow/building/coreclr/nativeaot.md
new file mode 100644
index 00000000000000..576939fe9e3b70
--- /dev/null
+++ b/docs/workflow/building/coreclr/nativeaot.md
@@ -0,0 +1,83 @@
+# Native AOT Developer Workflow
+
+The Native AOT toolchain can be currently built for Linux (x64/arm64), macOS (x64) and Windows (x64/arm64).
+
+## High Level Overview
+
+Native AOT is a stripped down version of the CoreCLR runtime specialized for ahead of time compilation, with an accompanying ahead of time compiler.
+
+The main components of the toolchain are:
+
+* The AOT compiler (ILC/ILCompiler) built on a shared codebase with crossgen2 (src/coreclr/tools/aot). Where crossgen2 generates ReadyToRun modules that contain code and data structures for the CoreCLR runtime, ILC generates code and self-describing datastructures for a stripped down version of CoreCLR into object files. The object files use platform specific file formats (COFF with CodeView on Windows, ELF with DWARF on Linux, and Mach-O with DWARF on macOS).
+* The stripped down CoreCLR runtime (NativeAOT specific files in src/coreclr/nativeaot/Runtime, the rest included from the src/coreclr). The stripped down runtime is built into a static library that is linked with object file generated by the AOT compiler using a platform-specific linker (link.exe on Windows, ld on Linux/macOS) to form a standalone executable.
+* The bootstrapper library (src/coreclr/nativeaot/Bootstrap). This is a small native library that contains the actual native `main()` entrypoint and bootstraps the runtime and dispatches to managed code. Two flavors of the bootstrapper are built - one for executables, and another for dynamic libraries.
+* The core libraries (src/coreclr/nativeaot): System.Private.CoreLib (corelib), System.Private.Reflection.* (the implementation of reflection), System.Private.TypeLoader (ability to load new types that were not generated statically).
+* The dotnet integration (src/coreclr/nativeaot/BuildIntegration). This is a set of .targets/.props files that hook into `dotnet publish` to run the AOT compiler and execute the platform linker.
+
+The AOT compiler typically takes the app, core libraries, and framework libraries as input. It then compiles the whole program into a single object file. Then the object file is linked to form a runnable executable. The executable is standalone (doesn't require a runtime), modulo any managed DllImports.
+
+The executable looks like a native executable, in the sense that it can be debugged with native debuggers and have full-fidelity access to locals, and stepping information.
+
+## Building
+
+- [Install pre-requisites](../../README.md#build-requirements)
+- Run `build[.cmd|.sh] clr+libs -rc [Debug|Release] -lc Release` from the repo root. This will restore nuget packages required for building and build the parts of the repo required for general CoreCLR development. Alternatively, instead of specifying `clr+libs`, you can specify `clr.jit+clr.tools+clr.nativeaotlibs+libs` which is more targeted and builds faster. Replace `clr.jit` with `clr.alljits` if you need to crosscompile.
+- [NOT PORTED OVER YET] The build will place the toolchain packages at `artifacts\packages\[Debug|Release]\Shipping`. To publish your project using these packages:
+ - [NOT PORTED OVER YET] Add the package directory to your `nuget.config` file. For example, replace `dotnet-experimental` line in `samples\HelloWorld\nuget.config` with ``
+ - [NOT PORTED OVER YET] Run `dotnet publish --packages pkg -r [win-x64|linux-x64|osx-64] -c [Debug|Release]` to publish your project. `--packages pkg` option restores the package into a local directory that is easy to cleanup once you are done. It avoids polluting the global nuget cache with your locally built dev package.
+- *Optional*. The ObjWriter component of the AOT compiler is not built by default. If you're working on ObjWriter or bringing up a new platform that doesn't have ObjWriter packages yet, as additional pre-requiresites you need to run `build[.cmd|.sh] clr.objwriter` from the repo root before building the product.
+
+## Visual Studio Solutions
+
+The repository has a number of Visual Studio Solutions files (`*.sln`) that are useful for editing parts of the repository. Build the repo from command line first before building using the solution files. Remember to select the appropriate configuration that you built. By default, `build.cmd` builds Debug x64 and so `Debug` and `x64` must be selected in the solution build configuration drop downs.
+
+- `src\coreclr\nativeaot\nativeaot.sln`. This solution is for the runtime libraries.
+- `src\coreclr\tools\aot\ilc.sln`. This solution is for the compiler.
+
+Typical workflow for working on the compiler:
+- Open `ilc.sln` in Visual Studio
+- Set "ILCompiler" project in solution explorer as your startup project
+- Set Working directory in the project Debug options to your test project directory, e.g. `C:\runtimelab\samples\HelloWorld`
+- Set Application arguments in the project Debug options to the response file that was generated by regular native aot publishing of your test project, e.g. `@obj\Release\net6.0\win-x64\native\HelloWorld.ilc.rsp`
+- Build & run using **F5**
+
+## Convenience Visual Studio "repro" project
+
+Typical native AOT runtime developer scenario workflow is to native AOT compile a short piece of C# and run it. The repo contains helper projects that make debugging the AOT compiler and the runtime easier.
+
+The workflow looks like this:
+
+- Build the repo using the Building instructions above
+- Open the ilc.sln solution described above. This solution contains the compiler, but also an unrelated project named "repro". This repro project is a small Hello World. You can place any piece of C# you would like to compile in it. Building the project will compile the source code into IL, but also generate a response file that is suitable to pass to the AOT compiler.
+- Make sure you set the solution configuration in VS to the configuration you just built (e.g. x64 Debug).
+- In the ILCompiler project properties, on the Debug tab, set the "Application arguments" to the generated response file. This will be a file such as "C:\runtime\artifacts\bin\repro\x64\Debug\compile-with-Release-libs.rsp". Prefix the path to the file with "@" to indicate this is a response file so that the "Application arguments" field looks like "@some\path\to\file.rsp".
+- Build & run ILCompiler using **F5**. This will compile the repro project into an `.obj` file. You can debug the compiler and set breakpoints in it at this point.
+- The last step is linking the object file into an executable so that we can launch the result of the AOT compilation.
+- Open the src\coreclr\tools\aot\ILCompiler\reproNative\reproNative.vcxproj project in Visual Studio. This project is configured to pick up the `.obj` file we just compiled and link it with the rest of the runtime.
+- Set the solution configuration to the tuple you've been using so far (e.g. x64 Debug)
+- Build & run using **F5**. This will run the platform linker to link the obj file with the runtime and launch it. At this point you can debug the runtime and the various System.Private libraries.
+
+## Running tests
+
+If you haven't built the tests yet, run `src\tests\build[.cmd|.sh] nativeaot [Debug|Release] tree nativeaot`. This will build the smoke tests only - they usually suffice to ensure the runtime and compiler is in a workable shape. To build all Pri-0 tests, drop the `tree nativeaot` parameter. The `Debug`/`Release` parameter should match the build configuration you used to build the runtime.
+
+To run all the tests that got built, run `src\tests\run.cmd runnativeaottests [Debug|Release]` on Windows, or `src/tests/run.sh --runnativeaottests [Debug|Release]` on Linux. The `Debug`/`Release` flag should match the flag that was passed to `build.cmd` in the previous step.
+
+To run an individual test (after it was built), navigate to the `artifacts\tests\coreclr\[Windows|Linux|OSX[.x64.[Debug|Release]\$path_to_test` directory. `$path_to_test` matches the subtree of `src\tests`. You should see a `[.cmd|.sh]` file there. This file is a script that will compile and launch the individual test for you. Before invoking the script, set the following environment variables:
+
+* CORE_ROOT=$repo_root\artifacts\tests\coreclr\[Windows|Linux|OSX[.x64.[Debug|Release]\Tests\Core_Root
+* RunNativeAot=1
+* __TestDotNetCmd=$repo_root\dotnet[.cmd|.sh]
+
+`$repo_root` is the root of your clone of the repo.
+
+By default the test suite will delete the build artifacts (Native AOT images and response files) if the test compiled successfully. If you want to keep these files instead of deleting them after test run, set the following environment variables and make sure you'll have enough disk space (tens of MB per test):
+
+* CLRTestNoCleanup=1
+
+For more advanced scenarios, look for at [Building Test Subsets](../../testing/coreclr/windows-test-instructions.md#building-test-subsets) and [Generating Core_Root](../../testing/coreclr/windows-test-instructions.md#generating-core_root)
+
+## Design Documentation
+
+- [ILC Compiler Architecture](../../../design/coreclr/botr/ilc-architecture.md)
+- [Managed Type System](../../../design/coreclr/botr/managed-type-system.md)
diff --git a/docs/workflow/debugging/coreclr/debugging-crossgen2.md b/docs/workflow/debugging/coreclr/debugging-aot-compilers.md
similarity index 64%
rename from docs/workflow/debugging/coreclr/debugging-crossgen2.md
rename to docs/workflow/debugging/coreclr/debugging-aot-compilers.md
index 4c37061943d43c..e2fd7f7e3bb905 100644
--- a/docs/workflow/debugging/coreclr/debugging-crossgen2.md
+++ b/docs/workflow/debugging/coreclr/debugging-aot-compilers.md
@@ -1,23 +1,25 @@
-How to Debug Crossgen2
+How to Debug CoreCLR AOT Compilers
=================
-Crossgen2 brings with it a number of new challenges for debugging the compilation process. Fortunately, in addition to challenges, crossgen2 is designed to enhance various parts of the debugging experience.
+CoreCLR comes with two AOT compilers that are built around a shared C# codebase - crossgen2 and ilc. Crossgen2 generates ReadyToRun images that are loadable into the JIT-based CoreCLR runtime. ILC generates platform-specific object files (COFF on Windows, ELF on Linux, Mach-O on macOS) that can be linked with the NativeAOT flavor of the CoreCLR runtime to form a self-contained executable or a shared library.
-Important concerns to be aware of when debugging Crossgen2
+The managed AOT compilers bring with them a number of new challenges for debugging the compilation process. Fortunately, in addition to challenges, the compilers are designed to enhance various parts of the debugging experience.
+
+Important concerns to be aware of when debugging the managed compilers
---------------------------------
-- Other than the JIT, Crossgen2 is a managed application
-- By default Crossgen2 uses a multi-core compilation strategy
-- A Crossgen2 process will have 2 copies of the JIT in the process at the same time, the one used to compile the target, and the one used to compile Crossgen2 itself.
-- Crossgen2 does not parse environment variables for controlling the JIT (or any other behavior), all behavior is controlled via the command line
-- The Crossgen2 command line as generated by the project system is quite complex
+- Other than the JIT, the AOT compilers are managed applications
+- By default the AOT compilers use a multi-core compilation strategy
+- A compilation process will have 2 copies of the JIT in the process at the same time, the one used to compile the target, and the one used to compile compiler itself.
+- The compilers don't parse environment variables for controlling the JIT (or any other behavior), all behavior is controlled via the command line
+- The AOT compiler command line as generated by the project system is quite complex
-Built in debugging aids in Crossgen2
+Built in debugging aids in the managed compilers
---------------------------------
-- When debugging a multi-threaded component of Crossgen2 and not investigating a multi-threading issue itself, it is generally advisable to disable the use of multiple threads.
-To do this use the `--parallelism 1` switch to specify that the maximum parallelism of the process shall be 1.
+- When debugging a multi-threaded component of the compiler and not investigating a multi-threading issue itself, it is generally advisable to disable the use of multiple threads.
+To do this use the `--parallelism 1` switch (for crossgen2) or `--singlethreaded` (for ILC) to specify that the maximum parallelism of the process shall be 1.
- When debugging the behavior of compiling a single method, the compiler may be instructed to only compile a single method. This is done via the various --singlemethod options
@@ -25,13 +27,13 @@ To do this use the `--parallelism 1` switch to specify that the maximum parallel
- `--singlemethodindex` is used in cases where the method signature is the only distinguishing factor about the method. An index is used instead of a series of descriptive arguments, as specifying a signature exactly is extraordinarily complicated.
- Repro args will look like the following ``--singlemethodtypename "Internal.Runtime.CompilerServices.Unsafe" --singlemethodname As --singlemethodindex 2 --singlemethodgenericarg "System.Runtime.Intrinsics.Vector256`1[[System.SByte]]" --singlemethodgenericarg "System.Runtime.Intrinsics.Vector256`1[[System.Double]]"``
-- Since Crossgen2 is by default multi-threaded, it produces results fairly quickly even when compiling using a Debug variant of the JIT. In general, when debugging JIT issues we recommend using the debug JIT regardless of which environment caused a problem.
+- Since the compilers are by default multi-threaded, they produce results fairly quickly even when compiling using a Debug variant of the JIT. In general, when debugging JIT issues we recommend using the debug JIT regardless of which environment caused a problem.
-- Crossgen2 supports nearly arbitrary cross-targetting, including OS and architecture cross targeting. The only restriction is that 32bit architecture cannot compile targetting a 64bit architecture. This allows the use of the debugging environment most convenient to the developer. In particular, if there is an issue which crosses the managed/native boundary, it is often convenient to debug using the mixed mode debugger on Windows X64.
- - If the correct set of assemblies/command line arguments are passed to the compiler Crossgen2 should produce binary identical output on all platforms.
+- The compilers support nearly arbitrary cross-targetting, including OS and architecture cross targeting. The only restriction is that 32bit architecture cannot compile targetting a 64bit architecture. This allows the use of the debugging environment most convenient to the developer. In particular, if there is an issue which crosses the managed/native boundary, it is often convenient to debug using the mixed mode debugger on Windows X64.
+ - If the correct set of assemblies/command line arguments are passed to the compiler, it should produce binary identical output on all platforms.
- The compiler does not check the OS/Architecture specified for input assemblies, which allows compiling using a non-architecture/OS matched version of the framework to target an arbitrary target. While this isn't useful for producing the diagnosing all issues, it can be cheaply used to identify the general behavior of a change on the full swath of supported architectures.
-Control compilation behavior by using the `--targetos` and `--targetarch` switches. The default behavior is to target the crossgen2's own OS/Arch pair, but all 64bit versions of crossgen2 are capable of targetting arbitrary OS/Arch combinations.
+Control compilation behavior by using the `--targetos` and `--targetarch` switches. The default behavior is to target the compiler's own OS/Arch pair, but all 64bit versions of the compilers are capable of targetting arbitrary OS/Arch combinations.
At the time of writing the current supported sets of valid arguments are:
| Command line arguments
| --- |
@@ -49,28 +51,38 @@ At the time of writing the current supported sets of valid arguments are:
- When using the NgenDump feature of the JIT, disable parallelism as described above or specify a single method to be compiled. Otherwise, output from multiple functions will be interleaved and inscrutable.
-- Since there are 2 jits in the process, when debugging in the JIT, if the source files match up, there is a decent chance that a native debugger will stop at unfortunate and unexpected locations. This is extremely annoying, and to combat this, we generally recommend making a point of using a runtime which doesn't exactly match that of the crossgen2 in use. However, if that isn't feasible, it is also possible to disable symbol loading in most native debuggers. For instance, in Visual Studio, one would use the "Specify excluded modules" feature.
+- Since there are 2 jits in the process, when debugging in the JIT, if the source files match up, there is a decent chance that a native debugger will stop at unfortunate and unexpected locations. This is extremely annoying, and to combat this, we generally recommend making a point of using a runtime which doesn't exactly match that of the compiler in use. However, if that isn't feasible, it is also possible to disable symbol loading in most native debuggers. For instance, in Visual Studio, one would use the "Specify excluded modules" feature.
-- Crossgen2 identifies the JIT to use by the means of a naming convention. By default it will use a JIT located in the same directory as the crossgen2.dll file. In addition there is support for a `--jitpath` switch to use a specific JIT. This option is intended to support A/B testing by the JIT team. The `--jitpath` option should only be used if the jit interface has not been changed. The JIT specified by the `--jitpath` switch must be compatible with the current settings of the `--targetos` and `--targetarch` switches.
+- The compiler identifies the JIT to use by the means of a naming convention. By default it will use a JIT located in the same directory as the crossgen2.dll file. In addition there is support for a `--jitpath` switch to use a specific JIT. This option is intended to support A/B testing by the JIT team. The `--jitpath` option should only be used if the jit interface has not been changed. The JIT specified by the `--jitpath` switch must be compatible with the current settings of the `--targetos` and `--targetarch` switches.
-- In parallel to the crossgen2 project, there is a tool known as r2rdump. This tool can be used to dump the contents of a produced image to examine what was actually produced in the final binary. It has a large multitude of options to control exactly what is dumped, but in general it is able to dump any image produced by crossgen2, and display its contents in a human readable fashion. Specify `--disasm` to display disassembly.
+- In parallel to the crossgen2 project, there is a tool known as r2rdump. This tool can be used to dump the contents of a produced ReadyToRun image to examine what was actually produced in the final binary. It has a large multitude of options to control exactly what is dumped, but in general it is able to dump any image produced by crossgen2, and display its contents in a human readable fashion. Specify `--disasm` to display disassembly.
-- If there is a need to debug the dependency graph of crossgen2 (which is a very rare need at this time), there is a visualization tool located in the corert repo. https://github.com/dotnet/corert/tree/master/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer To use that tool, get the sources from the CoreRT repo, compile it, and run it on Windows before the crossgen2 compilation begins. It will present a live view of the graph as it is generated and allow for exploration to determine why some node is in the graph. Every node in the graph has a unique id that is visible to this tool, and it can be used in parallel with a debugger to understand what is happening in the crossgen2 process. Changes to move this tool to a more commonly built location and improve the fairly horrible UI are encouraged.
+- If there is a need to debug the dependency graph of the compiler (which is a very rare need at this time), there is a visualization tool located in src\coreclr\tools\aot\DependencyGraphViewer. To use that tool, compile it, and run it on Windows before the compilation begins. It will present a live view of the graph as it is generated and allow for exploration to determine why some node is in the graph. Every node in the graph has a unique id that is visible to this tool, and it can be used in parallel with a debugger to understand what is happening in the compilation process. Changes to improve the fairly horrible UI are encouraged.
-- When used in the official build system, the set of arguments passed to crossgen2 is extremely complex, especially with regards to the set of reference paths (each assembly is specified individually). To make it easier to use crossgen2 from the command line manually the tool will accept wildcards in its parsing of references. Please note that on Unix that the shell will happily expand these arguments by itself, which will not work correctly. In those situations enclose the argument in quotes to prevent the shell expansion.
+- When used in the official build system, the set of arguments passed to the compiler are extremely complex, especially with regards to the set of reference paths (each assembly is specified individually). To make it easier to use crossgen2 from the command line manually the tool will accept wildcards in its parsing of references. Please note that on Unix that the shell will happily expand these arguments by itself, which will not work correctly. In those situations enclose the argument in quotes to prevent the shell expansion.
- Crossgen2 supports a `--map` and `--mapcsv` arguments to produce map files of the produced output. These are primarily used for diagnosing size issues, as they describe the generated file in fairly high detail, as well as providing a number of interesting statistics about the produced output.
+- ILC also supports the `--map` argument but the format is different from crossgen2 because the output format is different too.
+
- Diagnosing why a specific method failed to compile in crossgen2 can be done by passing the `--verbose` switch to crossgen2. This will print many things, but in particular it will print the reason why a compilation was aborted due to an R2R format limitation.
-- Crossgen2 can use either the version of dotnet that is used to build the product (as found by the dotnet.cmd or dotnet.sh script found in the root of the runtime repo) or it can use a sufficiently recent corerun.exe produced by constructing a test build. It is strongly recommended if using corerun.exe to use a release build of corerun for this purpose, as crossgen2 runs a very large amount of managed code. The version of corerun used does not need to come from the same build as the crossgen2.dll that is being debugging. In fact, I would recommnend using a different enlistment to build that corerun to avoid any confusion.
+- The compilers can use either the version of dotnet that is used to build the product (as found by the dotnet.cmd or dotnet.sh script found in the root of the runtime repo) or it can use a sufficiently recent corerun.exe produced by constructing a test build. It is strongly recommended if using corerun.exe to use a release build of corerun for this purpose, as crossgen2 runs a very large amount of managed code. The version of corerun used does not need to come from the same build as the crossgen2.dll/ilc.dll that is being debugging. In fact, I would recommnend using a different enlistment to build that corerun to avoid any confusion.
+
+- In the runtime testbed, each test can be commanded to compile with crossgen2 by using environment variables. Just set the `RunCrossgen2` variable to 1, and optionally set the `CompositeBuildMode` variable to 1 if you wish to see the R2R behavior with composite image creation.
-- In the runtime testbed, each test can be commanded to compile with crossgen2 by using environment variables. Just set the `RunCrossgen2` variable to 1, and optionally set the `CompositeBuildMode` variable to 1 if you wish to see the R2R behavior with composite image creation. By default this will simply use `dotnet` to run crossgen2. If you run the test batch script from the root of the enlistment on Windows this will just work; otherwise, you must set the `__TestDotNetCmd` environment variable to point at copy of `dotnet` or `corerun` that can run crossgen2. This is often the easiest way to run a simple test with crossgen2 for developers practiced in the CoreCLR testbed. See the example below of various techniques to use when diagnosing issues under crossgen2.
+- By default the runtime test bed will simply use `dotnet` to run the managed compiler. If you run the test batch script from the root of the enlistment on Windows this will just work; otherwise, you must set the `__TestDotNetCmd` environment variable to point at copy of `dotnet` or `corerun` that can run the compiler. This is often the easiest way to run a simple test with the AOT compiler for developers practiced in the CoreCLR testbed. See the example below of various techniques to use when diagnosing issues under crossgen2.
- When attempting to build crossgen2, you must build the clr.tools subset. If rebuilding a component of the JIT and wanting to use that in your inner loop, you must build as well with either the clr.jit or clr.alljits subsets. If the jit interface is changed, the clr.runtime subset must also be rebuilt.
- After completion of a product build, a functional copy of crossgen2.dll will be located in a bin directory in a path like `bin\coreclr\windows.x64.Debug\crossgen2`. After creating a test native layout via a command such as `src\tests\build generatelayoutonly` then there will be a copy of crossgen2 located in the `%CORE_ROOT%\crossgen2` directory. The version of crossgen2 in the test core_root directory will have the appropriate files for running under either an x64 dotnet.exe or under the target architecture. This was done to make it somewhat easier to do cross platform development, and assumes the primary development machine is x64,
+- The object files generated by the ILC compiler contain debug information for method bodies and types in the platform specific format (CodeView on Windows, DWARF elsewhere). They also contain unwinding information in the platform format. As a result of that, NativeAOT executables can be debugged with platform debuggers (VS, WinDbg, GDB, LLDB) without any SOS-like extensions. They can also be inspected using disassemblers and tools that deal with native code (dumpbin, Ghidra, IDA). Make sure to pass `-g` command line argument to enable debug info generation.
+
+- The ILC compiler typically compiles the whole program - it loosely corresponds to the composite mode of crossgen2. There is a multifile mode, where each managed assembly corresponds to a single object file, but this mode is not shipping.
+
+- The object files generated by the ILC compiler are written out using an LLVM-based object writer (src\coreclr\tools\aot\ObjWriter). The object writer uses the LLVM assembler APIs (APIs meant to be used by tools that convert textual assembly into machine code) to emit object files in PE/ELF/Mach-O formats. Normally the object writer is not built as part of the repo, but gets downloaded through NuGet. If you need to debug the object writer, you can build it by specifying `clr.objwriter` subset to the root build script. It takes about 5 minutes to compile the object writer.
+
Example of debugging a test application in Crossgen2
================================================
This example is to demonstrate debugging of a simple test in the CoreCLR testbed.
diff --git a/docs/workflow/debugging/coreclr/debugging.md b/docs/workflow/debugging/coreclr/debugging.md
index 3a11178ce90d0a..be4326ca66e077 100644
--- a/docs/workflow/debugging/coreclr/debugging.md
+++ b/docs/workflow/debugging/coreclr/debugging.md
@@ -73,10 +73,10 @@ The "COMPlus_EnableDiagnostics" environment variable can be used to disable mana
export COMPlus_EnableDiagnostics=0
-Debugging Crossgen2
+Debugging AOT compilers
==================================
-Debugging Crossgen2 is described in [this](debugging-crossgen2.md) document.
+Debugging AOT compilers is described in [this](debugging-aot-compilers.md) document.
Using Visual Studio Code
========================
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 4c5975082e6690..83ad7ff90ed386 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -18,77 +18,77 @@
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7dd
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7ddhttps://github.com/microsoft/vstest
@@ -138,37 +138,37 @@
https://github.com/dotnet/runtime-assets658e482c4af9a16cbe9ea0fae4c6e4281f1521b6
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0
-
+ https://github.com/dotnet/llvm-project
- 88d4fb233637975ba0deca837ad83c50d88303ac
+ 662aff66999c435aec09c58643e9fd703eadc3e0https://github.com/dotnet/runtime
@@ -214,9 +214,9 @@
https://github.com/dotnet/xharnessd5affc0d0361de14aa1ccbf5cad268d5873e3113
-
+ https://github.com/dotnet/arcade
- bcd6e007b9f53be0a7aff804d5c17ea7e179317b
+ 943d03f62955c771825dfa1f1bdeb8f853a2d7ddhttps://dev.azure.com/dnceng/internal/_git/dotnet-optimization
diff --git a/eng/Versions.props b/eng/Versions.props
index 6dcfa03fa00b92..22c9796bed7290 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -54,21 +54,21 @@
2.0.0-alpha.1.21525.11
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 2.5.1-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
- 7.0.0-beta.21613.2
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 2.5.1-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.1
+ 7.0.0-beta.21615.16.0.0-preview.1.102
@@ -174,14 +174,14 @@
7.0.0-alpha.1.21529.3
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
- 11.1.0-alpha.1.21613.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.1
+ 11.1.0-alpha.1.21615.17.0.0-alpha.1.21601.1$(MicrosoftNETWorkloadEmscriptenManifest70100Version)
diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index 0421f1f03ce0a9..5a20591cc025bb 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -447,7 +447,7 @@ if (CLR_CMAKE_HOST_UNIX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
add_compile_options(-arch arm64)
elseif(CLR_CMAKE_HOST_ARCH_AMD64)
- set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
+ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
add_compile_options(-arch x86_64)
else()
clr_unknown_arch()
diff --git a/eng/pipelines/common/evaluate-default-paths.yml b/eng/pipelines/common/evaluate-default-paths.yml
index 326cc76470341c..7b404e2c37b745 100644
--- a/eng/pipelines/common/evaluate-default-paths.yml
+++ b/eng/pipelines/common/evaluate-default-paths.yml
@@ -116,6 +116,11 @@ jobs:
- src/mono/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/*
- src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/*
- src/mono/mono/*
+ - subset: wasmdebuggertests
+ include:
+ - src/mono/wasm/debugger/*
+ - src/mono/wasm/runtime/*
+ - src/mono/mono/*
- ${{ if ne(parameters.extraSubsets, '') }}:
- ${{ parameters.extraSubsets }}
diff --git a/eng/pipelines/coreclr/jit-experimental.yml b/eng/pipelines/coreclr/jit-experimental.yml
index f6e4edd422bdeb..a50b2b6cb2856b 100644
--- a/eng/pipelines/coreclr/jit-experimental.yml
+++ b/eng/pipelines/coreclr/jit-experimental.yml
@@ -15,7 +15,11 @@ jobs:
jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml
buildConfig: checked
platforms:
+ - OSX_arm64
+ - OSX_x64
+ - Linux_arm64
- Linux_x64
+ - windows_arm64
- windows_x64
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
@@ -35,7 +39,11 @@ jobs:
jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml
buildConfig: checked
platforms:
+ - OSX_arm64
+ - OSX_x64
+ - Linux_arm64
- Linux_x64
+ - windows_arm64
- windows_x64
helixQueueGroup: ci
helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
diff --git a/eng/pipelines/coreclr/nativeaot-post-build-steps.yml b/eng/pipelines/coreclr/nativeaot-post-build-steps.yml
new file mode 100644
index 00000000000000..15f6f53de57bef
--- /dev/null
+++ b/eng/pipelines/coreclr/nativeaot-post-build-steps.yml
@@ -0,0 +1,35 @@
+parameters:
+ buildConfig: ''
+ archType: ''
+ osGroup: ''
+ osSubgroup: ''
+ platform: ''
+ pgoType: ''
+ runtimeVariant: ''
+ uploadTests: false
+ testFilter: tree nativeaot
+ runSingleFileTests: true
+
+steps:
+# Can't run arm/arm64 tests on x64 build machines
+- ${{ if and(ne(parameters.archType, 'arm'), ne(parameters.archType, 'arm64')) }}:
+
+ # Build coreclr native test output
+ - ${{ if eq(parameters.osGroup, 'windows') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/build.cmd nativeaot $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.testFilter }} /p:NativeAotMultimodule=true
+ displayName: Build tests
+ - ${{ if ne(parameters.osGroup, 'windows') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/build.sh nativeaot $(buildConfigUpper) ${{ parameters.archType }} '${{ parameters.testFilter }}'
+ displayName: Build tests
+
+ - ${{ if eq(parameters.runSingleFileTests, true) }}:
+ - ${{ if eq(parameters.osGroup, 'windows') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/run.cmd runnativeaottests $(buildConfigUpper) ${{ parameters.archType }}
+ displayName: Run tests in single file mode
+ - ${{ if ne(parameters.osGroup, 'windows') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/run.sh --runnativeaottests $(buildConfigUpper) ${{ parameters.archType }}
+ displayName: Run tests in single file mode
+
+ - ${{ if eq(parameters.osGroup, 'windows') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/run.sh runnativeaottests nativeaotmultimodule $(buildConfigUpper) ${{ parameters.archType }}
+ displayName: Run tests in multifile mode
diff --git a/eng/pipelines/coreclr/templates/helix-queues-setup.yml b/eng/pipelines/coreclr/templates/helix-queues-setup.yml
index 9516ac15aae5b3..1a5a3467587025 100644
--- a/eng/pipelines/coreclr/templates/helix-queues-setup.yml
+++ b/eng/pipelines/coreclr/templates/helix-queues-setup.yml
@@ -66,7 +66,7 @@ jobs:
# Linux musl x64
- ${{ if eq(parameters.platform, 'Linux_musl_x64') }}:
- ${{ if eq(variables['System.TeamProject'], 'public') }}:
- - (Alpine.314.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.14-helix-amd64-20210910135833-1848e19
+ - (Alpine.314.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.14-helix-amd64-20210910135833-1848e19
- ${{ if eq(variables['System.TeamProject'], 'internal') }}:
- (Alpine.314.Amd64)ubuntu.1604.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.14-helix-amd64-20210910135833-1848e19
@@ -92,7 +92,7 @@ jobs:
- (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673
- (Debian.11.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-helix-amd64-20210304164428-5a7c380
- Ubuntu.1804.Amd64.Open
- - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
+ - (Centos.8.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
- RedHat.7.Amd64.Open
- ${{ if eq(variables['System.TeamProject'], 'internal') }}:
- (Debian.10.Amd64)Ubuntu.1804.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index 6907934e069d69..ac074ab9903f8f 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -42,9 +42,9 @@ jobs:
# Linux musl x64
- ${{ if eq(parameters.platform, 'Linux_musl_x64') }}:
- - (Alpine.314.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.14-helix-amd64-20210910135833-1848e19
+ - (Alpine.314.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.14-helix-amd64-20210910135833-1848e19
- ${{ if eq(parameters.jobParameters.isFullMatrix, true) }}:
- - (Alpine.313.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.13-helix-amd64-20210910135845-8a6f4f3
+ - (Alpine.313.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.13-helix-amd64-20210910135845-8a6f4f3
# Linux musl arm64
- ${{ if and(eq(parameters.platform, 'Linux_musl_arm64'), eq(parameters.jobParameters.isFullMatrix, true)) }}:
@@ -55,28 +55,28 @@ jobs:
- ${{ if eq(parameters.platform, 'Linux_x64') }}:
- ${{ if and(eq(parameters.jobParameters.interpreter, ''), ne(parameters.jobParameters.isSingleFile, true)) }}:
- ${{ if and(eq(parameters.jobParameters.testScope, 'outerloop'), eq(parameters.jobParameters.runtimeFlavor, 'mono')) }}:
- - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
+ - (Centos.8.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
- RedHat.7.Amd64.Open
- SLES.15.Amd64.Open
- - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125
- - (Ubuntu.2110.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.10-helix-amd64-20211116135132-0f8d97e
- - (Debian.10.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006
+ - (Fedora.34.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125
+ - (Ubuntu.2110.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.10-helix-amd64-20211116135132-0f8d97e
+ - (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006
- ${{ if or(ne(parameters.jobParameters.testScope, 'outerloop'), ne(parameters.jobParameters.runtimeFlavor, 'mono')) }}:
- ${{ if eq(parameters.jobParameters.isFullMatrix, true) }}:
- - (Centos.7.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-mlnet-helix-20210714125435-dde38af
- - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
+ - (Centos.7.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-mlnet-helix-20210714125435-dde38af
+ - (Centos.8.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
- RedHat.7.Amd64.Open
- Ubuntu.1804.Amd64.Open
- SLES.12.Amd64.Open
- SLES.15.Amd64.Open
- - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125
- - (Ubuntu.2110.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.10-helix-amd64-20211116135132-0f8d97e
+ - (Fedora.34.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125
+ - (Ubuntu.2110.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.10-helix-amd64-20211116135132-0f8d97e
- (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673
- (Debian.11.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-helix-amd64-20210304164428-5a7c380
- - (Mariner.1.0.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-1.0-helix-20210528192219-92bf620
- - (openSUSE.15.2.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:opensuse-15.2-helix-amd64-20211018152525-9cc02fe
+ - (Mariner.1.0.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-1.0-helix-20210528192219-92bf620
+ - (openSUSE.15.2.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:opensuse-15.2-helix-amd64-20211018152525-9cc02fe
- ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}:
- - (Centos.7.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-mlnet-helix-20210714125435-dde38af
+ - (Centos.7.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-mlnet-helix-20210714125435-dde38af
- RedHat.7.Amd64.Open
- (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673
- Ubuntu.1804.Amd64.Open
diff --git a/eng/pipelines/runtime-manual.yml b/eng/pipelines/runtime-manual.yml
index 3be3efb85c9e2c..77f7b29cad8ac4 100644
--- a/eng/pipelines/runtime-manual.yml
+++ b/eng/pipelines/runtime-manual.yml
@@ -497,6 +497,69 @@ jobs:
creator: dotnet-bot
testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+# Wasm debugger tests
+- 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
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmdebuggertestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Mono_DebuggerTests
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ scenarios:
+ - wasmdebuggertests
+
+# Wasm debugger tests - windows
+- 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
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm_win
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmdebuggertestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Windows_wasm_DebuggerTests
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false /p:BrowserHost=windows
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows
+ scenarios:
+ - wasmdebuggertests
+
#
# Build the whole product using Mono for Android and run runtime tests with Android emulator
#
diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml
index 7a76fbcd117a78..79ed215e3a6393 100644
--- a/eng/pipelines/runtime-staging.yml
+++ b/eng/pipelines/runtime-staging.yml
@@ -616,6 +616,67 @@ jobs:
eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
+# Wasm debugger tests
+- 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
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmdebuggertestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Mono_DebuggerTests
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ scenarios:
+ - wasmdebuggertests
+
+# Wasm debugger tests - windows
+- 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
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm_win
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmdebuggertestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Windows_wasm_DebuggerTests
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false /p:BrowserHost=windows
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmdebuggertests.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows
+ scenarios:
+ - wasmdebuggertests
+
#
# CoreCLR Build for running Apple Silicon libraries-innerloop
#
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 04fca6cc817093..d49bf441a853b9 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -196,6 +196,75 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr_jit.containsChange'], true),
eq(variables['isFullMatrix'], true)))
+#
+# CoreCLR NativeAOT debug build and smoke tests
+# Only when CoreCLR is changed
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: debug
+ platforms:
+ - Linux_x64
+ - windows_x64
+ jobParameters:
+ testGroup: innerloop
+ timeoutInMinutes: 120
+ nameSuffix: NativeAOT
+ buildArgs: -s clr.jit+clr.tools+clr.nativeaotlibs+libs -rc $(_BuildConfig) -lc Release
+ extraStepsTemplate: /eng/pipelines/coreclr/nativeaot-post-build-steps.yml
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+
+#
+# CoreCLR NativeAOT checked build and smoke tests
+# Only when CoreCLR is changed
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: checked
+ platforms:
+ - windows_x64
+ jobParameters:
+ testGroup: innerloop
+ timeoutInMinutes: 120
+ nameSuffix: NativeAOT
+ buildArgs: -s clr.jit+clr.tools+clr.nativeaotlibs+libs -rc $(_BuildConfig) -lc Release
+ extraStepsTemplate: /eng/pipelines/coreclr/nativeaot-post-build-steps.yml
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+
+#
+# CoreCLR NativeAOT checked build and smoke tests
+# Only when CoreCLR is changed
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: release
+ platforms:
+ - Linux_x64
+ - windows_x64
+ - OSX_x64
+ jobParameters:
+ testGroup: innerloop
+ timeoutInMinutes: 120
+ nameSuffix: NativeAOT
+ buildArgs: -s clr.jit+clr.tools+clr.nativeaotlibs+libs -rc $(_BuildConfig) -lc Release
+ extraStepsTemplate: /eng/pipelines/coreclr/nativeaot-post-build-steps.yml
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
+ eq(variables['isFullMatrix'], true))
+
# Build and test clr tools
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/testing/scenarios/WasmDebuggerTestsJobsList.txt b/eng/testing/scenarios/WasmDebuggerTestsJobsList.txt
new file mode 100644
index 00000000000000..d4ef9dcbfc399b
--- /dev/null
+++ b/eng/testing/scenarios/WasmDebuggerTestsJobsList.txt
@@ -0,0 +1,18 @@
+DebuggerTests.ArrayTests
+DebuggerTests.AssignmentTests
+DebuggerTests.AsyncTests
+DebuggerTests.BadHarnessInitTests
+DebuggerTests.BreakpointTests
+DebuggerTests.CallFunctionOnTests
+DebuggerTests.CustomViewTests
+DebuggerTests.DateTimeTests
+DebuggerTests.DelegateTests
+DebuggerTests.EvaluateOnCallFrameTests
+DebuggerTests.ExceptionTests
+DebuggerTests.GetPropertiesTests
+DebuggerTests.HarnessTests
+DebuggerTests.MonoJsTests
+DebuggerTests.PointerTests
+DebuggerTests.SetVariableValueTests
+DebuggerTests.MiscTests
+DebuggerTests.SteppingTests
diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets
index 67a781d1058b67..866e80d596f3da 100644
--- a/eng/testing/workloads-testing.targets
+++ b/eng/testing/workloads-testing.targets
@@ -36,10 +36,17 @@
- <_DotNetInstallScriptPath Condition="!$([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.sh
- <_DotNetInstallScriptPath Condition=" $([MSBuild]::IsOSPlatform('windows'))">$(RepoRoot).dotnet\dotnet-install.ps1
+ <_DotNetInstallScriptName Condition="!$([MSBuild]::IsOSPlatform('windows'))">dotnet-install.sh
+ <_DotNetInstallScriptName Condition=" $([MSBuild]::IsOSPlatform('windows'))">dotnet-install.ps1
+
+ <_DotNetInstallScriptPath>$(ArtifactsObjDir)$(_DotNetInstallScriptName)
+
+
diff --git a/global.json b/global.json
index e9834c18cde33b..2dd889a46287e8 100644
--- a/global.json
+++ b/global.json
@@ -12,10 +12,10 @@
"python3": "3.7.1"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21613.2",
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21613.2",
- "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21613.2",
- "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21613.2",
+ "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21615.1",
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21615.1",
+ "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21615.1",
+ "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21615.1",
"Microsoft.Build.NoTargets": "3.1.0",
"Microsoft.Build.Traversal": "3.0.23",
"Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21612.5"
diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake
index ffadad35521002..50aab221c8032e 100644
--- a/src/coreclr/clrdefinitions.cmake
+++ b/src/coreclr/clrdefinitions.cmake
@@ -179,9 +179,9 @@ endif(FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION)
add_definitions(-DFEATURE_SVR_GC)
add_definitions(-DFEATURE_SYMDIFF)
add_compile_definitions(FEATURE_TIERED_COMPILATION)
-if (CLR_CMAKE_TARGET_ARCH_AMD64)
+if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64)
add_compile_definitions(FEATURE_ON_STACK_REPLACEMENT)
-endif (CLR_CMAKE_TARGET_ARCH_AMD64)
+endif (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64)
add_compile_definitions(FEATURE_PGO)
if (CLR_CMAKE_TARGET_WIN32)
add_definitions(-DFEATURE_TYPEEQUIVALENCE)
diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp
index 8bd5037f7ebe5e..895d2afdf395a9 100644
--- a/src/coreclr/gc/unix/gcenv.unix.cpp
+++ b/src/coreclr/gc/unix/gcenv.unix.cpp
@@ -67,7 +67,6 @@
#include
#include
-#if defined(HOST_ARM64)
#include
#include
extern "C"
@@ -84,7 +83,6 @@ extern "C"
abort(); \
} \
} while (false)
-#endif // defined(HOST_ARM64)
#endif // __APPLE__
@@ -372,7 +370,7 @@ bool GCToOSInterface::Initialize()
{
s_flushUsingMemBarrier = TRUE;
}
-#if !(defined(TARGET_OSX) && defined(HOST_ARM64))
+#ifndef TARGET_OSX
else
{
assert(g_helperPage == 0);
@@ -404,7 +402,7 @@ bool GCToOSInterface::Initialize()
return false;
}
}
-#endif // !(defined(TARGET_OSX) && defined(HOST_ARM64))
+#endif // !TARGET_OSX
InitializeCGroup();
@@ -544,7 +542,7 @@ void GCToOSInterface::FlushProcessWriteBuffers()
status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex);
assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock");
}
-#if defined(TARGET_OSX) && defined(HOST_ARM64)
+#ifdef TARGET_OSX
else
{
mach_msg_type_number_t cThreads;
@@ -570,7 +568,7 @@ void GCToOSInterface::FlushProcessWriteBuffers()
machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t));
CHECK_MACH("vm_deallocate()", machret);
}
-#endif // defined(TARGET_OSX) && defined(HOST_ARM64)
+#endif // TARGET_OSX
}
// Break into a debugger. Uses a compiler intrinsic if one is available,
diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp
index 02cf63915874fb..9781299c8049ef 100644
--- a/src/coreclr/hosts/corerun/corerun.hpp
+++ b/src/coreclr/hosts/corerun/corerun.hpp
@@ -473,6 +473,12 @@ namespace pal
return false;
}
+ // Ignore directories
+ if (S_ISDIR(sb.st_mode))
+ {
+ return false;
+ }
+
// Verify that the path points to a file
if (!S_ISREG(sb.st_mode))
{
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index f6f341e56f32a6..5a1df5f2af32e3 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -362,8 +362,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_NEWSFAST_ALIGN8, // allocator for small, non-finalizer, non-array object, 8 byte aligned
CORINFO_HELP_NEWSFAST_ALIGN8_VC,// allocator for small, value class, 8 byte aligned
CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE, // allocator for small, finalizable, non-array object, 8 byte aligned
- CORINFO_HELP_NEW_MDARR, // multi-dim array helper (with or without lower bounds - dimensions passed in as vararg)
- CORINFO_HELP_NEW_MDARR_NONVARARG,// multi-dim array helper (with or without lower bounds - dimensions passed in as unmanaged array)
+ CORINFO_HELP_NEW_MDARR,// multi-dim array helper (with or without lower bounds - dimensions passed in as unmanaged array)
CORINFO_HELP_NEWARR_1_DIRECT, // helper for any one dimensional array creation
CORINFO_HELP_NEWARR_1_OBJ, // optimized 1-D object arrays
CORINFO_HELP_NEWARR_1_VC, // optimized 1-D value class arrays
@@ -622,7 +621,6 @@ enum CorInfoHelpSig
CORINFO_HELP_SIG_8_STACK,
CORINFO_HELP_SIG_12_STACK,
CORINFO_HELP_SIG_16_STACK,
- CORINFO_HELP_SIG_8_VA, //2 arguments plus varargs
CORINFO_HELP_SIG_EBPCALL, //special calling convention that uses EDX and
//EBP as arguments
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index a32a05ad530b19..8b6285020afb8d 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 = { /* 29ff53ef-3c61-4fc4-bdab-82e39a3d7970 */
- 0x29ff53ef,
- 0x3c61,
- 0x4fc4,
- {0xbd, 0xab, 0x82, 0xe3, 0x9a, 0x3d, 0x79, 0x70}
+constexpr GUID JITEEVersionIdentifier = { /* 1d61ee87-b3be-48ae-a12e-2fb9b5b1cee7 */
+ 0x1d61ee87,
+ 0xb3be,
+ 0x48ae,
+ {0xa1, 0x2e, 0x2f, 0xb9, 0xb5, 0xb1, 0xce, 0xe7}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 4d1fc11f0e8cb2..b9a1e3941da389 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -75,8 +75,7 @@
DYNAMICJITHELPER(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_New, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_NEWSFAST_ALIGN8_VC, NULL, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE, NULL, CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_NEW_MDARR, JIT_NewMDArr,CORINFO_HELP_SIG_8_VA)
- JITHELPER(CORINFO_HELP_NEW_MDARR_NONVARARG, JIT_NewMDArrNonVarArg,CORINFO_HELP_SIG_4_STACK)
+ JITHELPER(CORINFO_HELP_NEW_MDARR, JIT_NewMDArr,CORINFO_HELP_SIG_4_STACK)
JITHELPER(CORINFO_HELP_NEWARR_1_DIRECT, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
diff --git a/src/coreclr/inc/patchpointinfo.h b/src/coreclr/inc/patchpointinfo.h
index d6bd2b6aee9a41..effcdeddceb051 100644
--- a/src/coreclr/inc/patchpointinfo.h
+++ b/src/coreclr/inc/patchpointinfo.h
@@ -13,8 +13,9 @@
// --------------------------------------------------------------------------------
// Describes information needed to make an OSR transition
// - location of Il-visible locals and other important state on the
-// original (Tier0) method frame
-// - total size of the original frame, and SP-FP delta
+// original (Tier0) method frame, with respect to top of frame
+// (hence these offsets will be negative as stack grows down)
+// - total size of the original frame
//
// Currently the patchpoint info is independent of the IL offset of the patchpoint.
//
@@ -33,9 +34,9 @@ struct PatchpointInfo
}
// Initialize
- void Initialize(unsigned localCount, int fpToSpDelta)
+ void Initialize(unsigned localCount, int totalFrameSize)
{
- m_fpToSpDelta = fpToSpDelta;
+ m_totalFrameSize = totalFrameSize;
m_numberOfLocals = localCount;
m_genericContextArgOffset = -1;
m_keptAliveThisOffset = -1;
@@ -43,16 +44,30 @@ struct PatchpointInfo
m_monitorAcquiredOffset = -1;
}
+ // Copy
+ void Copy(const PatchpointInfo* original)
+ {
+ m_genericContextArgOffset = original->m_genericContextArgOffset;
+ m_keptAliveThisOffset = original->m_keptAliveThisOffset;
+ m_securityCookieOffset = original->m_securityCookieOffset;
+ m_monitorAcquiredOffset = original->m_monitorAcquiredOffset;
+
+ for (unsigned i = 0; i < original->m_numberOfLocals; i++)
+ {
+ m_offsetAndExposureData[i] = original->m_offsetAndExposureData[i];
+ }
+ }
+
// Total size of this patchpoint info record, in bytes
unsigned PatchpointInfoSize() const
{
return ComputeSize(m_numberOfLocals);
}
- // FP to SP delta of the original method
- int FpToSpDelta() const
+ // Total frame size of the original method
+ int TotalFrameSize() const
{
- return m_fpToSpDelta;
+ return m_totalFrameSize;
}
// Number of locals in the original method (including special locals)
@@ -154,7 +169,7 @@ struct PatchpointInfo
};
unsigned m_numberOfLocals;
- int m_fpToSpDelta;
+ int m_totalFrameSize;
int m_genericContextArgOffset;
int m_keptAliveThisOffset;
int m_securityCookieOffset;
diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h
index a9b27973eaff30..25bd45d376e8fa 100644
--- a/src/coreclr/inc/readytorun.h
+++ b/src/coreclr/inc/readytorun.h
@@ -297,7 +297,6 @@ enum ReadyToRunHelper
READYTORUN_HELPER_Unbox = 0x5A,
READYTORUN_HELPER_Unbox_Nullable = 0x5B,
READYTORUN_HELPER_NewMultiDimArr = 0x5C,
- READYTORUN_HELPER_NewMultiDimArr_NonVarArg = 0x5D,
// Helpers used with generic handle lookup cases
READYTORUN_HELPER_NewObject = 0x60,
diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h
index ea3ea684b3626c..66e2d4a3b164e3 100644
--- a/src/coreclr/inc/readytorunhelpers.h
+++ b/src/coreclr/inc/readytorunhelpers.h
@@ -42,7 +42,6 @@ HELPER(READYTORUN_HELPER_Box_Nullable, CORINFO_HELP_BOX_NULLABLE,
HELPER(READYTORUN_HELPER_Unbox, CORINFO_HELP_UNBOX, )
HELPER(READYTORUN_HELPER_Unbox_Nullable, CORINFO_HELP_UNBOX_NULLABLE, )
HELPER(READYTORUN_HELPER_NewMultiDimArr, CORINFO_HELP_NEW_MDARR, )
-HELPER(READYTORUN_HELPER_NewMultiDimArr_NonVarArg, CORINFO_HELP_NEW_MDARR_NONVARARG, )
HELPER(READYTORUN_HELPER_NewObject, CORINFO_HELP_NEWFAST, )
HELPER(READYTORUN_HELPER_NewArray, CORINFO_HELP_NEWARR_1_DIRECT, )
diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp
index 1ae2176674a7d8..b842e1363fed62 100644
--- a/src/coreclr/jit/assertionprop.cpp
+++ b/src/coreclr/jit/assertionprop.cpp
@@ -1261,7 +1261,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
AssertionDsc assertion = {OAK_INVALID};
assert(assertion.assertionKind == OAK_INVALID);
- if (op1->gtOper == GT_ARR_BOUNDS_CHECK)
+ if (op1->OperIs(GT_BOUNDS_CHECK))
{
if (assertionKind == OAK_NO_THROW)
{
@@ -2583,7 +2583,7 @@ void Compiler::optAssertionGen(GenTree* tree)
assertionInfo = optCreateAssertion(tree->AsArrLen()->ArrRef(), nullptr, OAK_NOT_EQUAL);
break;
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
if (!optLocalAssertionProp)
{
assertionInfo = optCreateAssertion(tree, nullptr, OAK_NO_THROW);
@@ -4053,8 +4053,7 @@ GenTree* Compiler::optAssertionProp_Comma(ASSERT_VALARG_TP assertions, GenTree*
{
// Remove the bounds check as part of the GT_COMMA node since we need parent pointer to remove nodes.
// When processing visits the bounds check, it sets the throw kind to None if the check is redundant.
- if ((tree->gtGetOp1()->OperGet() == GT_ARR_BOUNDS_CHECK) &&
- ((tree->gtGetOp1()->gtFlags & GTF_ARR_BOUND_INBND) != 0))
+ if (tree->gtGetOp1()->OperIs(GT_BOUNDS_CHECK) && ((tree->gtGetOp1()->gtFlags & GTF_CHK_INDEX_INBND) != 0))
{
optRemoveCommaBasedRangeCheck(tree, stmt);
return optAssertionProp_Update(tree, tree, stmt);
@@ -4391,7 +4390,7 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
return nullptr;
}
- assert(tree->gtOper == GT_ARR_BOUNDS_CHECK);
+ assert(tree->OperIs(GT_BOUNDS_CHECK));
#ifdef FEATURE_ENABLE_NO_RANGE_CHECKS
if (JitConfig.JitNoRangeChks())
@@ -4403,7 +4402,7 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
gtDispTree(tree, nullptr, nullptr, true);
}
#endif // DEBUG
- tree->gtFlags |= GTF_ARR_BOUND_INBND;
+ tree->gtFlags |= GTF_CHK_INDEX_INBND;
return nullptr;
}
#endif // FEATURE_ENABLE_NO_RANGE_CHECKS
@@ -4514,7 +4513,7 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree
// Defer actually removing the tree until processing reaches its parent comma, since
// optRemoveCommaBasedRangeCheck needs to rewrite the whole comma tree.
- arrBndsChk->gtFlags |= GTF_ARR_BOUND_INBND;
+ arrBndsChk->gtFlags |= GTF_CHK_INDEX_INBND;
return nullptr;
}
@@ -4610,7 +4609,7 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
case GT_NULLCHECK:
return optAssertionProp_Ind(assertions, tree, stmt);
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
return optAssertionProp_BndsChk(assertions, tree, stmt);
case GT_COMMA:
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 343bcb138300bb..1e734193850eee 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -255,6 +255,11 @@ class CodeGen final : public CodeGenInterface
void genEstablishFramePointer(int delta, bool reportUnwindData);
void genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbered, RegState* regState);
void genEnregisterIncomingStackArgs();
+#if defined(TARGET_ARM64)
+ void genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed);
+#else
+ void genEnregisterOSRArgsAndLocals();
+#endif
void genCheckUseBlockInit();
#if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD)
void genClearStackVec3ArgUpperBits();
diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp
index db45c98b68e63c..988dfa412c7d86 100644
--- a/src/coreclr/jit/codegenarm64.cpp
+++ b/src/coreclr/jit/codegenarm64.cpp
@@ -20,6 +20,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "lower.h"
#include "gcinfo.h"
#include "gcinfoencoder.h"
+#include "patchpointinfo.h"
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -1113,9 +1114,20 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
if (genFuncletInfo.fiFrameType == 1)
{
- GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, genFuncletInfo.fiSpDelta1,
- INS_OPTS_PRE_INDEX);
- compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ // With OSR we may see large values for fiSpDelta1
+ // (we really need to probe the frame, sigh)
+ if (compiler->opts.IsOSR())
+ {
+ 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);
+ }
+ else
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, genFuncletInfo.fiSpDelta1,
+ INS_OPTS_PRE_INDEX);
+ compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ }
maskSaveRegsInt &= ~(RBM_LR | RBM_FP); // We've saved these now
@@ -1141,9 +1153,20 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
}
else if (genFuncletInfo.fiFrameType == 3)
{
- GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, genFuncletInfo.fiSpDelta1,
- INS_OPTS_PRE_INDEX);
- compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ // With OSR we may see large values for fiSpDelta1
+ // (we really need to probe the frame, sigh)
+ if (compiler->opts.IsOSR())
+ {
+ 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);
+ }
+ else
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_stp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, genFuncletInfo.fiSpDelta1,
+ INS_OPTS_PRE_INDEX);
+ compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ }
maskSaveRegsInt &= ~(RBM_LR | RBM_FP); // We've saved these now
}
@@ -1171,17 +1194,25 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
if ((genFuncletInfo.fiFrameType == 3) || (genFuncletInfo.fiFrameType == 5))
{
- // Note that genFuncletInfo.fiSpDelta2 is always a negative value
- assert(genFuncletInfo.fiSpDelta2 < 0);
+ // Note that genFuncletInfo.fiSpDelta2 is always a non-positive value
+ assert(genFuncletInfo.fiSpDelta2 <= 0);
// generate sub SP,SP,imm
- genStackPointerAdjustment(genFuncletInfo.fiSpDelta2, REG_R2, nullptr, /* reportUnwindData */ true);
+ if (genFuncletInfo.fiSpDelta2 < 0)
+ {
+ genStackPointerAdjustment(genFuncletInfo.fiSpDelta2, REG_R2, nullptr, /* reportUnwindData */ true);
+ }
+ else
+ {
+ // we will only see fiSpDelta2 == 0 for osr funclets
+ assert(compiler->opts.IsOSR());
+ }
}
// This is the end of the OS-reported prolog for purposes of unwinding
compiler->unwindEndProlog();
- // If there is no PSPSym (CoreRT ABI), we are done. Otherwise, we need to set up the PSPSym in the functlet frame.
+ // If there is no PSPSym (CoreRT ABI), we are done. Otherwise, we need to set up the PSPSym in the funclet frame.
if (compiler->lvaPSPSym != BAD_VAR_NUM)
{
if (isFilter)
@@ -1252,11 +1283,19 @@ void CodeGen::genFuncletEpilog()
if ((genFuncletInfo.fiFrameType == 3) || (genFuncletInfo.fiFrameType == 5))
{
- // Note that genFuncletInfo.fiSpDelta2 is always a negative value
- assert(genFuncletInfo.fiSpDelta2 < 0);
+ // Note that genFuncletInfo.fiSpDelta2 is always a non-positive value
+ assert(genFuncletInfo.fiSpDelta2 <= 0);
// generate add SP,SP,imm
- genStackPointerAdjustment(-genFuncletInfo.fiSpDelta2, REG_R2, nullptr, /* reportUnwindData */ true);
+ if (genFuncletInfo.fiSpDelta2 < 0)
+ {
+ genStackPointerAdjustment(-genFuncletInfo.fiSpDelta2, REG_R2, nullptr, /* reportUnwindData */ true);
+ }
+ else
+ {
+ // we should only zee zero SpDelta2 with osr.
+ assert(compiler->opts.IsOSR());
+ }
}
regMaskTP regsToRestoreMask = maskRestoreRegsInt | maskRestoreRegsFloat;
@@ -1269,9 +1308,21 @@ void CodeGen::genFuncletEpilog()
if (genFuncletInfo.fiFrameType == 1)
{
- GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, -genFuncletInfo.fiSpDelta1,
- INS_OPTS_POST_INDEX);
- compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ // With OSR we may see large values for fiSpDelta1
+ //
+ if (compiler->opts.IsOSR())
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, 0);
+ compiler->unwindSaveRegPair(REG_FP, REG_LR, 0);
+
+ genStackPointerAdjustment(-genFuncletInfo.fiSpDelta1, REG_SCRATCH, nullptr, /* reportUnwindData */ true);
+ }
+ else
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, -genFuncletInfo.fiSpDelta1,
+ INS_OPTS_POST_INDEX);
+ compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ }
assert(genFuncletInfo.fiSpDelta2 == 0);
assert(genFuncletInfo.fiSP_to_FPLR_save_delta == 0);
@@ -1293,9 +1344,21 @@ void CodeGen::genFuncletEpilog()
}
else if (genFuncletInfo.fiFrameType == 3)
{
- GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, -genFuncletInfo.fiSpDelta1,
- INS_OPTS_POST_INDEX);
- compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ // With OSR we may see large values for fiSpDelta1
+ //
+ if (compiler->opts.IsOSR())
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, 0);
+ compiler->unwindSaveRegPair(REG_FP, REG_LR, 0);
+
+ genStackPointerAdjustment(-genFuncletInfo.fiSpDelta1, REG_SCRATCH, nullptr, /* reportUnwindData */ true);
+ }
+ else
+ {
+ GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, -genFuncletInfo.fiSpDelta1,
+ INS_OPTS_POST_INDEX);
+ compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, genFuncletInfo.fiSpDelta1);
+ }
}
else if (genFuncletInfo.fiFrameType == 4)
{
@@ -1346,14 +1409,23 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
// The frame size and offsets must be finalized
assert(compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT);
- genFuncletInfo.fiFunction_CallerSP_to_FP_delta = genCallerSPtoFPdelta();
+ unsigned const PSPSize = (compiler->lvaPSPSym != BAD_VAR_NUM) ? REGSIZE_BYTES : 0;
+
+ // Because a method and funclets must have the same caller-relative PSPSym offset,
+ // if there is a PSPSym, we have to pad the funclet frame size for OSR.
+ //
+ unsigned osrPad = 0;
+ if (compiler->opts.IsOSR() && (PSPSize > 0))
+ {
+ osrPad = compiler->info.compPatchpointInfo->TotalFrameSize();
+ }
+
+ genFuncletInfo.fiFunction_CallerSP_to_FP_delta = genCallerSPtoFPdelta() - osrPad;
regMaskTP rsMaskSaveRegs = regSet.rsMaskCalleeSaved;
assert((rsMaskSaveRegs & RBM_LR) != 0);
assert((rsMaskSaveRegs & RBM_FP) != 0);
- unsigned PSPSize = (compiler->lvaPSPSym != BAD_VAR_NUM) ? REGSIZE_BYTES : 0;
-
unsigned saveRegsCount = genCountBits(rsMaskSaveRegs);
unsigned saveRegsPlusPSPSize = saveRegsCount * REGSIZE_BYTES + PSPSize;
if (compiler->info.compIsVarArgs)
@@ -1362,23 +1434,24 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
// so that they are contiguous with the incoming stack arguments.
saveRegsPlusPSPSize += MAX_REG_ARG * REGSIZE_BYTES;
}
- unsigned saveRegsPlusPSPSizeAligned = roundUp(saveRegsPlusPSPSize, STACK_ALIGN);
+
+ unsigned const saveRegsPlusPSPSizeAligned = roundUp(saveRegsPlusPSPSize, STACK_ALIGN);
assert(compiler->lvaOutgoingArgSpaceSize % REGSIZE_BYTES == 0);
- unsigned outgoingArgSpaceAligned = roundUp(compiler->lvaOutgoingArgSpaceSize, STACK_ALIGN);
+ unsigned const outgoingArgSpaceAligned = roundUp(compiler->lvaOutgoingArgSpaceSize, STACK_ALIGN);
- unsigned maxFuncletFrameSizeAligned = saveRegsPlusPSPSizeAligned + outgoingArgSpaceAligned;
+ unsigned const maxFuncletFrameSizeAligned = saveRegsPlusPSPSizeAligned + osrPad + outgoingArgSpaceAligned;
assert((maxFuncletFrameSizeAligned % STACK_ALIGN) == 0);
int SP_to_FPLR_save_delta;
int SP_to_PSP_slot_delta;
int CallerSP_to_PSP_slot_delta;
- unsigned funcletFrameSize = saveRegsPlusPSPSize + compiler->lvaOutgoingArgSpaceSize;
- unsigned funcletFrameSizeAligned = roundUp(funcletFrameSize, STACK_ALIGN);
+ unsigned const funcletFrameSize = saveRegsPlusPSPSize + osrPad + compiler->lvaOutgoingArgSpaceSize;
+ unsigned const funcletFrameSizeAligned = roundUp(funcletFrameSize, STACK_ALIGN);
assert(funcletFrameSizeAligned <= maxFuncletFrameSizeAligned);
- unsigned funcletFrameAlignmentPad = funcletFrameSizeAligned - funcletFrameSize;
+ unsigned const funcletFrameAlignmentPad = funcletFrameSizeAligned - funcletFrameSize;
assert((funcletFrameAlignmentPad == 0) || (funcletFrameAlignmentPad == REGSIZE_BYTES));
if (maxFuncletFrameSizeAligned <= 512)
@@ -1391,8 +1464,8 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
SP_to_FPLR_save_delta -= MAX_REG_ARG * REGSIZE_BYTES;
}
- SP_to_PSP_slot_delta = compiler->lvaOutgoingArgSpaceSize + funcletFrameAlignmentPad;
- CallerSP_to_PSP_slot_delta = -(int)saveRegsPlusPSPSize;
+ SP_to_PSP_slot_delta = compiler->lvaOutgoingArgSpaceSize + funcletFrameAlignmentPad + osrPad;
+ CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize);
genFuncletInfo.fiFrameType = 4;
}
@@ -1400,7 +1473,7 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
{
SP_to_FPLR_save_delta = compiler->lvaOutgoingArgSpaceSize;
SP_to_PSP_slot_delta = SP_to_FPLR_save_delta + 2 /* FP, LR */ * REGSIZE_BYTES + funcletFrameAlignmentPad;
- CallerSP_to_PSP_slot_delta = -(int)(saveRegsPlusPSPSize - 2 /* FP, LR */ * REGSIZE_BYTES);
+ CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize - 2 /* FP, LR */ * REGSIZE_BYTES);
if (compiler->lvaOutgoingArgSpaceSize == 0)
{
@@ -1432,7 +1505,7 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
SP_to_PSP_slot_delta =
compiler->lvaOutgoingArgSpaceSize + funcletFrameAlignmentPad + saveRegsPlusPSPAlignmentPad;
- CallerSP_to_PSP_slot_delta = -(int)saveRegsPlusPSPSize;
+ CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSize);
genFuncletInfo.fiFrameType = 5;
}
@@ -1440,13 +1513,13 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
{
SP_to_FPLR_save_delta = outgoingArgSpaceAligned;
SP_to_PSP_slot_delta = SP_to_FPLR_save_delta + 2 /* FP, LR */ * REGSIZE_BYTES + saveRegsPlusPSPAlignmentPad;
- CallerSP_to_PSP_slot_delta =
- -(int)(saveRegsPlusPSPSizeAligned - 2 /* FP, LR */ * REGSIZE_BYTES - saveRegsPlusPSPAlignmentPad);
+ CallerSP_to_PSP_slot_delta = -(int)(osrPad + saveRegsPlusPSPSizeAligned - 2 /* FP, LR */ * REGSIZE_BYTES -
+ saveRegsPlusPSPAlignmentPad);
genFuncletInfo.fiFrameType = 3;
}
- genFuncletInfo.fiSpDelta1 = -(int)saveRegsPlusPSPSizeAligned;
+ genFuncletInfo.fiSpDelta1 = -(int)(osrPad + saveRegsPlusPSPSizeAligned);
genFuncletInfo.fiSpDelta2 = -(int)outgoingArgSpaceAligned;
assert(genFuncletInfo.fiSpDelta1 + genFuncletInfo.fiSpDelta2 == -(int)maxFuncletFrameSizeAligned);
@@ -1468,7 +1541,10 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
printf(" Save regs: ");
dspRegMask(genFuncletInfo.fiSaveRegs);
printf("\n");
- printf(" Function CallerSP-to-FP delta: %d\n", genFuncletInfo.fiFunction_CallerSP_to_FP_delta);
+ if (compiler->opts.IsOSR())
+ {
+ printf(" OSR Pad: %d\n", osrPad);
+ }
printf(" SP to FP/LR save location delta: %d\n", genFuncletInfo.fiSP_to_FPLR_save_delta);
printf(" SP to PSP slot delta: %d\n", genFuncletInfo.fiSP_to_PSP_slot_delta);
printf(" SP to callee-saved area delta: %d\n", genFuncletInfo.fiSP_to_CalleeSave_delta);
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index 19a24869146225..daa0eb29df1440 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -20,6 +20,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "lower.h"
#include "gcinfo.h"
#include "emit.h"
+#include "patchpointinfo.h"
//------------------------------------------------------------------------
// genStackPointerConstantAdjustment: add a specified constant value to the stack pointer.
@@ -462,13 +463,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
instGen(INS_nop);
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
genRangeCheck(treeNode);
break;
@@ -584,6 +579,12 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed)
return;
}
+ if (compiler->opts.IsOSR() && compiler->info.compPatchpointInfo->HasSecurityCookie())
+ {
+ // Security cookie is on original frame and was initialized there.
+ return;
+ }
+
if (compiler->gsGlobalSecurityCookieAddr == nullptr)
{
noway_assert(compiler->gsGlobalSecurityCookieVal != 0);
@@ -1418,11 +1419,11 @@ void CodeGen::genMultiRegStoreToSIMDLocal(GenTreeLclVar* lclNode)
#endif // FEATURE_SIMD
//------------------------------------------------------------------------
-// genRangeCheck: generate code for GT_ARR_BOUNDS_CHECK node.
+// genRangeCheck: generate code for GT_BOUNDS_CHECK node.
//
void CodeGen::genRangeCheck(GenTree* oper)
{
- noway_assert(oper->OperIsBoundsCheck());
+ noway_assert(oper->OperIs(GT_BOUNDS_CHECK));
GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
GenTree* arrLen = bndsChk->GetArrayLength();
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 237a22c6782f0f..0c479168f4fee4 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -4530,11 +4530,9 @@ void CodeGen::genEnregisterIncomingStackArgs()
}
#endif
- // OSR handles this specially
- if (compiler->opts.IsOSR())
- {
- return;
- }
+ // OSR handles this specially -- see genEnregisterOSRArgsAndLocals
+ //
+ assert(!compiler->opts.IsOSR());
assert(compiler->compGeneratingProlog);
@@ -5391,6 +5389,32 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog)
unreached();
}
}
+
+ // For OSR, we must also adjust the SP to remove the Tier0 frame.
+ //
+ if (compiler->opts.IsOSR())
+ {
+ PatchpointInfo* const patchpointInfo = compiler->info.compPatchpointInfo;
+ const int tier0FrameSize = patchpointInfo->TotalFrameSize();
+ JITDUMP("Extra SP adjust for OSR to pop off Tier0 frame: %d bytes\n", tier0FrameSize);
+
+ // Tier0 size may exceed simple immediate. We're in the epilog so not clear if we can
+ // use a scratch reg. So just do two subtracts if necessary.
+ //
+ int spAdjust = tier0FrameSize;
+ if (!GetEmitter()->emitIns_valid_imm_for_add(tier0FrameSize, EA_PTRSIZE))
+ {
+ const int lowPart = spAdjust & 0xFFF;
+ const int highPart = spAdjust - lowPart;
+ assert(GetEmitter()->emitIns_valid_imm_for_add(highPart, EA_PTRSIZE));
+ GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, highPart);
+ compiler->unwindAllocStack(highPart);
+ spAdjust = lowPart;
+ }
+ assert(GetEmitter()->emitIns_valid_imm_for_add(spAdjust, EA_PTRSIZE));
+ GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, spAdjust);
+ compiler->unwindAllocStack(spAdjust);
+ }
}
#elif defined(TARGET_XARCH)
@@ -6162,136 +6186,196 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg,
inst_ST_RV(ins_Store(TYP_I_IMPL), tempThis, 0, genGetZeroReg(initReg, pInitRegZeroed), TYP_I_IMPL);
}
}
+}
- // Initialize args and locals for OSR. Note this may include promoted fields.
- if (compiler->opts.IsOSR())
- {
- PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo;
+//-----------------------------------------------------------------------------
+// genEnregisterOSRArgsAndLocals: Initialize any enregistered args or locals
+// that get values from the tier0 frame.
+//
+// Arguments:
+// initReg -- scratch register to use if needed
+// pInitRegZeroed -- [IN,OUT] if init reg is zero (on entry/exit)
+//
+#if defined(TARGET_ARM64)
+void CodeGen::genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed)
+#else
+void CodeGen::genEnregisterOSRArgsAndLocals()
+#endif
+{
+ assert(compiler->opts.IsOSR());
+ PatchpointInfo* const patchpointInfo = compiler->info.compPatchpointInfo;
- // basic sanity checks (make sure we're OSRing the right method)
- assert(patchpointInfo->NumberOfLocals() == compiler->info.compLocalsCount);
+ // basic sanity checks (make sure we're OSRing the right method)
+ assert(patchpointInfo->NumberOfLocals() == compiler->info.compLocalsCount);
- const int originalFrameSize = patchpointInfo->FpToSpDelta();
- const unsigned patchpointInfoLen = patchpointInfo->NumberOfLocals();
+ const int originalFrameSize = patchpointInfo->TotalFrameSize();
+ const unsigned patchpointInfoLen = patchpointInfo->NumberOfLocals();
- for (unsigned varNum = 0; varNum < compiler->lvaCount; varNum++)
+ for (unsigned varNum = 0; varNum < compiler->lvaCount; varNum++)
+ {
+ if (!compiler->lvaIsOSRLocal(varNum))
{
- if (!compiler->lvaIsOSRLocal(varNum))
- {
- continue;
- }
+ // This local was not part of the tier0 method's state.
+ // No work required.
+ //
+ continue;
+ }
- LclVarDsc* const varDsc = compiler->lvaGetDesc(varNum);
+ LclVarDsc* const varDsc = compiler->lvaGetDesc(varNum);
- if (!varDsc->lvIsInReg())
- {
- JITDUMP("---OSR--- V%02u in memory\n", varNum);
- continue;
- }
-
- // We currently don't expect to see multi-reg args in OSR methods, as struct
- // promotion is disabled and so any struct arg just uses the spilled location
- // on the original frame.
- //
- // If we ever enable promotion we'll need to generalize what follows to copy each
- // field from the original frame to its OSR home.
+ if (!varDsc->lvIsInReg())
+ {
+ // For args/locals in memory, the OSR frame will continue to access
+ // that memory location. No work required.
//
- assert(!varDsc->lvIsMultiRegArg);
+ JITDUMP("---OSR--- V%02u in memory\n", varNum);
+ continue;
+ }
- if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
- {
- JITDUMP("---OSR--- V%02u (reg) not live at entry\n", varNum);
- continue;
- }
+ // This local was part of the live tier0 state and is enregistered in the
+ // OSR method. Initialize the register from the right frame slot.
+ //
+ // We currently don't expect to see enregistered multi-reg args in OSR methods,
+ // as struct promotion is disabled. So any struct arg just uses the location
+ // on the tier0 frame.
+ //
+ // If we ever enable promotion we'll need to generalize what follows to copy each
+ // field from the tier0 frame to its OSR home.
+ //
+ assert(!varDsc->lvIsMultiRegArg);
- int fieldOffset = 0;
- unsigned lclNum = varNum;
+ if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
+ {
+ // This arg or local is not live at entry to the OSR method.
+ // No work required.
+ //
+ JITDUMP("---OSR--- V%02u (reg) not live at entry\n", varNum);
+ continue;
+ }
- if (varDsc->lvIsStructField)
- {
- lclNum = varDsc->lvParentLcl;
- assert(lclNum < patchpointInfoLen);
+ int fieldOffset = 0;
+ unsigned lclNum = varNum;
- fieldOffset = varDsc->lvFldOffset;
- JITDUMP("---OSR--- V%02u is promoted field of V%02u at offset %d\n", varNum, lclNum, fieldOffset);
- }
+ if (varDsc->lvIsStructField)
+ {
+ lclNum = varDsc->lvParentLcl;
+ assert(lclNum < patchpointInfoLen);
- // Note we are always reading from the original frame here
- //
- const var_types lclTyp = varDsc->GetActualRegisterType();
- const emitAttr size = emitTypeSize(lclTyp);
- const int stkOffs = patchpointInfo->Offset(lclNum) + fieldOffset;
+ fieldOffset = varDsc->lvFldOffset;
+ JITDUMP("---OSR--- V%02u is promoted field of V%02u at offset %d\n", varNum, lclNum, fieldOffset);
+ }
- // Original frames always use frame pointers, so
- // stkOffs is the original frame-relative offset
- // to the variable.
- //
- // We need to determine the stack or frame-pointer relative
- // offset for this variable in the current frame.
- //
- // If current frame does not use a frame pointer, we need to
- // add the SP-to-FP delta of this frame and the SP-to-FP delta
- // of the original frame; that translates from this frame's
- // stack pointer the old frame frame pointer.
- //
- // We then add the original frame's frame-pointer relative
- // offset (note this offset is usually negative -- the stack
- // grows down, so locals are below the frame pointer).
- //
- // /-----original frame-----/
- // / return address /
- // / saved RBP --+ / <--- Original frame ptr --+
- // / ... | / |
- // / ... (stkOffs) / |
- // / ... | / |
- // / variable --+ / |
- // / ... / (original frame sp-fp delta)
- // / ... / |
- // /-----OSR frame ---------/ |
- // / pseudo return address / --+
- // / ... / |
- // / ... / (this frame sp-fp delta)
- // / ... / |
- // /------------------------/ <--- Stack ptr --+
- //
- // If the current frame is using a frame pointer, we need to
- // add the SP-to-FP delta of/ the original frame and then add
- // the original frame's frame-pointer relative offset.
- //
- // /-----original frame-----/
- // / return address /
- // / saved RBP --+ / <--- Original frame ptr --+
- // / ... | / |
- // / ... (stkOffs) / |
- // / ... | / |
- // / variable --+ / |
- // / ... / (original frame sp-fp delta)
- // / ... / |
- // /-----OSR frame ---------/ |
- // / pseudo return address / --+
- // / saved RBP / <--- Frame ptr --+
- // / ... /
- // / ... /
- // / ... /
- // /------------------------/
-
- int offset = originalFrameSize + stkOffs;
-
- if (isFramePointerUsed())
- {
- // also adjust for saved RPB on this frame
- offset += TARGET_POINTER_SIZE;
- }
- else
- {
- offset += genSPtoFPdelta();
- }
+ // Note we are always reading from the tier0 frame here
+ //
+ const var_types lclTyp = varDsc->GetActualRegisterType();
+ const emitAttr size = emitTypeSize(lclTyp);
+ const int stkOffs = patchpointInfo->Offset(lclNum) + fieldOffset;
- JITDUMP("---OSR--- V%02u (reg) old rbp offset %d old frame %d this frame sp-fp %d new offset %d (%02xH)\n",
- varNum, stkOffs, originalFrameSize, genSPtoFPdelta(), offset, offset);
+#if defined(TARGET_AMD64)
- GetEmitter()->emitIns_R_AR(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset);
+ // Original frames always use frame pointers, so
+ // stkOffs is the tier0 frame's frame-relative offset
+ // to the variable.
+ //
+ // We need to determine the stack or frame-pointer relative
+ // offset for this variable in the current frame.
+ //
+ // If current frame does not use a frame pointer, we need to
+ // add the SP-to-FP delta of this frame and the SP-to-FP delta
+ // of the original frame; that translates from this frame's
+ // stack pointer the old frame frame pointer.
+ //
+ // We then add the original frame's frame-pointer relative
+ // offset (note this offset is usually negative -- the stack
+ // grows down, so locals are below the frame pointer).
+ //
+ // /-----original frame-----/
+ // / return address /
+ // / saved RBP --+ / <--- Original frame ptr --+
+ // / ... | / |
+ // / ... (stkOffs) / |
+ // / ... | / |
+ // / variable --+ / |
+ // / ... / (original frame sp-fp delta)
+ // / ... / |
+ // /-----OSR frame ---------/ |
+ // / pseudo return address / --+
+ // / ... / |
+ // / ... / (this frame sp-fp delta)
+ // / ... / |
+ // /------------------------/ <--- Stack ptr --+
+ //
+ // If the current frame is using a frame pointer, we need to
+ // add the SP-to-FP delta of/ the original frame and then add
+ // the original frame's frame-pointer relative offset.
+ //
+ // /-----original frame-----/
+ // / return address /
+ // / saved RBP --+ / <--- Original frame ptr --+
+ // / ... | / |
+ // / ... (stkOffs) / |
+ // / ... | / |
+ // / variable --+ / |
+ // / ... / (original frame sp-fp delta)
+ // / ... / |
+ // /-----OSR frame ---------/ |
+ // / pseudo return address / --+
+ // / saved RBP / <--- Frame ptr --+
+ // / ... /
+ // / ... /
+ // / ... /
+ // /------------------------/
+ //
+ int offset = originalFrameSize + stkOffs;
+
+ if (isFramePointerUsed())
+ {
+ // also adjust for saved RPB on this frame
+ offset += TARGET_POINTER_SIZE;
+ }
+ else
+ {
+ offset += genSPtoFPdelta();
}
+
+ JITDUMP("---OSR--- V%02u (reg) old rbp offset %d old frame %d this frame sp-fp %d new offset %d (%02xH)\n",
+ varNum, stkOffs, originalFrameSize, genSPtoFPdelta(), offset, offset);
+
+ GetEmitter()->emitIns_R_AR(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset);
+
+#elif defined(TARGET_ARM64)
+
+ // Patchpoint offset is from top of Tier0 frame
+ //
+ // We need to determine the frame-pointer relative
+ // offset for this variable in the osr frame.
+ //
+ // First add the Tier0 frame size
+ //
+ const int tier0FrameSize = compiler->info.compPatchpointInfo->TotalFrameSize();
+
+ // then add the OSR frame size
+ //
+ const int osrFrameSize = genTotalFrameSize();
+
+ // then subtract OSR SP-FP delta
+ //
+ const int osrSpToFpDelta = genSPtoFPdelta();
+
+ // | => tier0 top of frame relative
+ // | + => tier0 bottom of frame relative
+ // | | + => osr bottom of frame (sp) relative
+ // | | | - => osr fp relative
+ // | | | |
+ const int offset = stkOffs + tier0FrameSize + osrFrameSize - osrSpToFpDelta;
+
+ JITDUMP("---OSR--- V%02u (reg) Tier0 virtual offset %d OSR frame size %d OSR sp-fp "
+ "delta %d total offset %d (0x%x)\n",
+ varNum, stkOffs, osrFrameSize, osrSpToFpDelta, offset, offset);
+
+ genInstrWithConstant(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset, initReg);
+ *pInitRegZeroed = false;
+#endif
}
}
@@ -6930,18 +7014,22 @@ void CodeGen::genFnProlog()
psiBegProlog();
}
-#if defined(TARGET_XARCH)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
// For OSR there is a "phantom prolog" to account for the actions taken
- // in the original frame that impact RBP and RSP on entry to the OSR method.
+ // in the tier0 frame that impact FP and SP on entry to the OSR method.
if (compiler->opts.IsOSR())
{
- PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo;
- const int originalFrameSize = patchpointInfo->FpToSpDelta();
+ PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo;
+ const int tier0FrameSize = patchpointInfo->TotalFrameSize();
+#if defined(TARGET_AMD64)
+ // FP is tier0 method's FP.
compiler->unwindPush(REG_FPBASE);
- compiler->unwindAllocStack(originalFrameSize);
- }
#endif
+ // SP is tier0 method's SP.
+ compiler->unwindAllocStack(tier0FrameSize);
+ }
+#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64)
#ifdef DEBUG
@@ -7477,14 +7565,30 @@ void CodeGen::genFnProlog()
* Take care of register arguments first
*/
- // Update the arg initial register locations.
- compiler->lvaUpdateArgsWithInitialReg();
-
// Home incoming arguments and generate any required inits.
// OSR handles this by moving the values from the original frame.
//
- if (!compiler->opts.IsOSR())
+ // Update the arg initial register locations.
+ //
+ if (compiler->opts.IsOSR())
{
+ // For OSR we defer updating "initial reg" for args until
+ // we've set the live-in regs with values from the Tier0 frame.
+ //
+ // Otherwise we'll do some of these fetches twice.
+ //
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#if defined(TARGET_ARM64)
+ genEnregisterOSRArgsAndLocals(initReg, &initRegZeroed);
+#else
+ genEnregisterOSRArgsAndLocals();
+#endif
+ compiler->lvaUpdateArgsWithInitialReg();
+ }
+ else
+ {
+ compiler->lvaUpdateArgsWithInitialReg();
+
auto assignIncomingRegisterArgs = [this, initReg, &initRegZeroed](RegState* regState) {
if (regState->rsCalleeRegArgMaskLiveIn)
{
@@ -7520,10 +7624,10 @@ void CodeGen::genFnProlog()
#else
assignIncomingRegisterArgs(&intRegState);
#endif
- }
- // Home the incoming arguments.
- genEnregisterIncomingStackArgs();
+ // Home the incoming arguments.
+ genEnregisterIncomingStackArgs();
+ }
/* Initialize any must-init registers variables now */
@@ -8166,21 +8270,21 @@ void CodeGen::genFnEpilog(BasicBlock* block)
}
#endif // TARGET_AMD64
- // Extra OSR adjust to get to where RBP was saved by the original frame, and
- // restore RBP.
+ // Extra OSR adjust to get to where RBP was saved by the tier0 frame to restore RBP.
//
// Note the other callee saves made in that frame are dead, the OSR method
// will save and restore what it needs.
if (compiler->opts.IsOSR())
{
- PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo;
- const int originalFrameSize = patchpointInfo->FpToSpDelta();
+ PatchpointInfo* const patchpointInfo = compiler->info.compPatchpointInfo;
+ const int tier0FrameSize = patchpointInfo->TotalFrameSize();
- // Use add since we know the SP-to-FP delta of the original method.
+ // Simply add since we know frame size is the SP-to-FP delta of the tier0 method plus
+ // the extra slot pushed by the runtime when we simulate calling the OSR method.
//
- // If we ever allow the original method to have localloc this will
- // need to change.
- inst_RV_IV(INS_add, REG_SPBASE, originalFrameSize, EA_PTRSIZE);
+ // If we ever support OSR from tier0 methods with localloc, this will need to change.
+ //
+ inst_RV_IV(INS_add, REG_SPBASE, tier0FrameSize, EA_PTRSIZE);
inst_RV(INS_pop, REG_EBP, TYP_I_IMPL);
}
}
@@ -8291,21 +8395,21 @@ void CodeGen::genFnEpilog(BasicBlock* block)
genPopCalleeSavedRegisters();
#ifdef TARGET_AMD64
- // Extra OSR adjust to get to where RBP was saved by the original frame.
+ // Extra OSR adjust to get to where RBP was saved by the tier0 frame.
//
// Note the other callee saves made in that frame are dead, the current method
// will save and restore what it needs.
if (compiler->opts.IsOSR())
{
- PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo;
- const int originalFrameSize = patchpointInfo->FpToSpDelta();
+ PatchpointInfo* const patchpointInfo = compiler->info.compPatchpointInfo;
+ const int tier0FrameSize = patchpointInfo->TotalFrameSize();
// Use add since we know the SP-to-FP delta of the original method.
// We also need to skip over the slot where we pushed RBP.
//
// If we ever allow the original method to have localloc this will
// need to change.
- inst_RV_IV(INS_add, REG_SPBASE, originalFrameSize + TARGET_POINTER_SIZE, EA_PTRSIZE);
+ inst_RV_IV(INS_add, REG_SPBASE, tier0FrameSize + TARGET_POINTER_SIZE, EA_PTRSIZE);
}
assert(!needMovEspEbp); // "mov esp, ebp" is not allowed in AMD64 epilogs
@@ -9310,6 +9414,11 @@ void CodeGen::genSetPSPSym(regNumber initReg, bool* pInitRegZeroed)
int SPtoCallerSPdelta = -genCallerSPtoInitialSPdelta();
+ if (compiler->opts.IsOSR())
+ {
+ SPtoCallerSPdelta += compiler->info.compPatchpointInfo->TotalFrameSize();
+ }
+
// We will just use the initReg since it is an available register
// and we are probably done using it anyway...
regNumber regTmp = initReg;
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 7b50388ad3138b..7da0bd72856251 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -1742,13 +1742,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
GetEmitter()->emitIns_Nop(1);
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
genRangeCheck(treeNode);
break;
@@ -3864,7 +3858,7 @@ void CodeGen::genCodeForCmpXchg(GenTreeCmpXchg* tree)
// generate code for BoundsCheck nodes
void CodeGen::genRangeCheck(GenTree* oper)
{
- noway_assert(oper->OperIsBoundsCheck());
+ noway_assert(oper->OperIs(GT_BOUNDS_CHECK));
GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
GenTree* arrIndex = bndsChk->GetIndex();
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 3e9059eb156af7..94adb74f6ba989 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -5340,11 +5340,38 @@ void Compiler::generatePatchpointInfo()
const unsigned patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount);
PatchpointInfo* const patchpointInfo = (PatchpointInfo*)info.compCompHnd->allocateArray(patchpointInfoSize);
- // The +TARGET_POINTER_SIZE here is to account for the extra slot the runtime
+ // Patchpoint offsets always refer to "virtual frame offsets".
+ //
+ // For x64 this falls out because Tier0 frames are always FP frames, and so the FP-relative
+ // offset is what we want.
+ //
+ // For arm64, if the frame pointer is not at the top of the frame, we need to adjust the
+ // offset.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if defined(TARGET_AMD64)
+ // We add +TARGET_POINTER_SIZE here is to account for the slot that Jit_Patchpoint
// creates when it simulates calling the OSR method (the "pseudo return address" slot).
- patchpointInfo->Initialize(info.compLocalsCount, codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE);
+ // This is effectively a new slot at the bottom of the Tier0 frame.
+ //
+ const int totalFrameSize = codeGen->genTotalFrameSize() + TARGET_POINTER_SIZE;
+ const int offsetAdjust = 0;
+#elif defined(TARGET_ARM64)
+ // SP is not manipulated by calls so no frame size adjustment needed.
+ // Local Offsets may need adjusting, if FP is at bottom of frame.
+ //
+ const int totalFrameSize = codeGen->genTotalFrameSize();
+ const int offsetAdjust = codeGen->genSPtoFPdelta() - totalFrameSize;
+#else
+ NYI("patchpoint info generation");
+ const int offsetAdjust = 0;
+ const int totalFrameSize = 0;
+#endif
+
+ patchpointInfo->Initialize(info.compLocalsCount, totalFrameSize);
- JITDUMP("--OSR--- FP-SP delta is %d\n", patchpointInfo->FpToSpDelta());
+ JITDUMP("--OSR--- Total Frame Size %d, local offset adjust is %d\n", patchpointInfo->TotalFrameSize(),
+ offsetAdjust);
// We record offsets for all the "locals" here. Could restrict
// this to just the IL locals with some extra logic, and save a bit of space,
@@ -5358,7 +5385,7 @@ void Compiler::generatePatchpointInfo()
assert(varDsc->lvFramePointerBased);
// Record FramePtr relative offset (no localloc yet)
- patchpointInfo->SetOffset(lclNum, varDsc->GetStackOffset());
+ patchpointInfo->SetOffset(lclNum, varDsc->GetStackOffset() + offsetAdjust);
// Note if IL stream contained an address-of that potentially leads to exposure.
// This bit of IL may be skipped by OSR partial importation.
@@ -5367,7 +5394,7 @@ void Compiler::generatePatchpointInfo()
patchpointInfo->SetIsExposed(lclNum);
}
- JITDUMP("--OSR-- V%02u is at offset %d%s\n", lclNum, patchpointInfo->Offset(lclNum),
+ JITDUMP("--OSR-- V%02u is at virtual offset %d%s\n", lclNum, patchpointInfo->Offset(lclNum),
patchpointInfo->IsExposed(lclNum) ? " (exposed)" : "");
}
@@ -5376,31 +5403,31 @@ void Compiler::generatePatchpointInfo()
if (lvaReportParamTypeArg())
{
const int offset = lvaCachedGenericContextArgOffset();
- patchpointInfo->SetGenericContextArgOffset(offset);
- JITDUMP("--OSR-- cached generic context offset is FP %d\n", patchpointInfo->GenericContextArgOffset());
+ patchpointInfo->SetGenericContextArgOffset(offset + offsetAdjust);
+ JITDUMP("--OSR-- cached generic context virtual offset is %d\n", patchpointInfo->GenericContextArgOffset());
}
if (lvaKeepAliveAndReportThis())
{
const int offset = lvaCachedGenericContextArgOffset();
- patchpointInfo->SetKeptAliveThisOffset(offset);
- JITDUMP("--OSR-- kept-alive this offset is FP %d\n", patchpointInfo->KeptAliveThisOffset());
+ patchpointInfo->SetKeptAliveThisOffset(offset + offsetAdjust);
+ JITDUMP("--OSR-- kept-alive this virtual offset is %d\n", patchpointInfo->KeptAliveThisOffset());
}
if (compGSReorderStackLayout)
{
assert(lvaGSSecurityCookie != BAD_VAR_NUM);
LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie);
- patchpointInfo->SetSecurityCookieOffset(varDsc->GetStackOffset());
- JITDUMP("--OSR-- security cookie V%02u offset is FP %d\n", lvaGSSecurityCookie,
+ patchpointInfo->SetSecurityCookieOffset(varDsc->GetStackOffset() + offsetAdjust);
+ JITDUMP("--OSR-- security cookie V%02u virtual offset is %d\n", lvaGSSecurityCookie,
patchpointInfo->SecurityCookieOffset());
}
if (lvaMonAcquired != BAD_VAR_NUM)
{
LclVarDsc* const varDsc = lvaGetDesc(lvaMonAcquired);
- patchpointInfo->SetMonitorAcquiredOffset(varDsc->GetStackOffset());
- JITDUMP("--OSR-- monitor acquired V%02u offset is FP %d\n", lvaMonAcquired,
+ patchpointInfo->SetMonitorAcquiredOffset(varDsc->GetStackOffset() + offsetAdjust);
+ JITDUMP("--OSR-- monitor acquired V%02u virtual offset is %d\n", lvaMonAcquired,
patchpointInfo->MonitorAcquiredOffset());
}
@@ -6363,6 +6390,47 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
compInitDebuggingInfo();
+ // If are an altjit and have patchpoint info, we might need to tweak the frame size
+ // so it's plausible for the altjit architecture.
+ //
+ if (!info.compMatchedVM && compileFlags->IsSet(JitFlags::JIT_FLAG_OSR))
+ {
+ assert(info.compLocalsCount == info.compPatchpointInfo->NumberOfLocals());
+ const int totalFrameSize = info.compPatchpointInfo->TotalFrameSize();
+
+ int frameSizeUpdate = 0;
+
+#if defined(TARGET_AMD64)
+ if ((totalFrameSize % 16) != 8)
+ {
+ frameSizeUpdate = 8;
+ }
+#elif defined(TARGET_ARM64)
+ if ((totalFrameSize % 16) != 0)
+ {
+ frameSizeUpdate = 8;
+ }
+#endif
+ if (frameSizeUpdate != 0)
+ {
+ JITDUMP("Mismatched altjit + OSR -- updating tier0 frame size from %d to %d\n", totalFrameSize,
+ totalFrameSize + frameSizeUpdate);
+
+ // Allocate a local copy with altered frame size.
+ //
+ const unsigned patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount);
+ PatchpointInfo* const newInfo =
+ (PatchpointInfo*)getAllocator(CMK_Unknown).allocate(patchpointInfoSize);
+
+ newInfo->Initialize(info.compLocalsCount, totalFrameSize + frameSizeUpdate);
+ newInfo->Copy(info.compPatchpointInfo);
+
+ // Swap it in place.
+ //
+ info.compPatchpointInfo = newInfo;
+ }
+ }
+
#ifdef DEBUG
if (compIsForInlining())
{
@@ -6494,6 +6562,24 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
{
const bool patchpointsOK = compCanHavePatchpoints(&reason);
assert(patchpointsOK || (reason != nullptr));
+
+#ifdef DEBUG
+ // Optionally disable OSR by method hash.
+ //
+ if (patchpointsOK && compHasBackwardJump)
+ {
+ static ConfigMethodRange JitEnableOsrRange;
+ JitEnableOsrRange.EnsureInit(JitConfig.JitEnableOsrRange());
+ const unsigned hash = impInlineRoot()->info.compMethodHash();
+ if (!JitEnableOsrRange.Contains(hash))
+ {
+ JITDUMP("Disabling OSR -- Method hash 0x%08x not within range ", hash);
+ JITDUMPEXEC(JitEnableOsrRange.Dump());
+ JITDUMP("\n");
+ reason = "OSR disabled by JitEnableOsrRange";
+ }
+ }
+#endif
}
if (reason != nullptr)
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index 8fad38af36f846..1b3c4d8768f1f0 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -2678,6 +2678,11 @@ inline bool Compiler::fgIsThrowHlpBlk(BasicBlock* block)
return false;
}
+ if (!block->IsLIR() && (block->lastStmt() == nullptr))
+ {
+ return false;
+ }
+
// Special check blocks will always end in a throw helper call.
//
GenTree* const call = block->lastNode();
@@ -4694,15 +4699,15 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
#ifdef FEATURE_ON_STACK_REPLACEMENT
if (compLocallocSeen)
{
- whyNot = "localloc";
+ whyNot = "OSR can't handle localloc";
}
else if (compHasBackwardJumpInHandler)
{
- whyNot = "loop in handler";
+ whyNot = "OSR can't handle loop in handler";
}
else if (opts.IsReversePInvoke())
{
- whyNot = "reverse pinvoke";
+ whyNot = "OSR can't handle reverse pinvoke";
}
#else
whyNot = "OSR feature not defined in build";
diff --git a/src/coreclr/jit/earlyprop.cpp b/src/coreclr/jit/earlyprop.cpp
index e4b29df2166dd9..f00b3f6e3ad6e6 100644
--- a/src/coreclr/jit/earlyprop.cpp
+++ b/src/coreclr/jit/earlyprop.cpp
@@ -288,13 +288,13 @@ GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree, LocalNumberToNullCheck
return nullptr;
}
- // When replacing GT_ARR_LENGTH nodes with constants we can end up with GT_ARR_BOUNDS_CHECK
+ // When replacing GT_ARR_LENGTH nodes with constants we can end up with GT_BOUNDS_CHECK
// nodes that have constant operands and thus can be trivially proved to be useless. It's
// better to remove these range checks here, otherwise they'll pass through assertion prop
// (creating useless (c1 < c2)-like assertions) and reach RangeCheck where they are finally
// removed. Common patterns like new int[] { x, y, z } benefit from this.
- if ((tree->gtNext != nullptr) && tree->gtNext->OperIs(GT_ARR_BOUNDS_CHECK))
+ if ((tree->gtNext != nullptr) && tree->gtNext->OperIs(GT_BOUNDS_CHECK))
{
GenTreeBoundsChk* check = tree->gtNext->AsBoundsChk();
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index c5ca3255d54fbf..9f0a2cf6a897e2 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -2738,13 +2738,29 @@ void emitter::emitSplit(emitLocation* startLoc,
reportCandidate = false;
}
+ // Don't report a zero-size candidate. This will only occur in a stress mode with JitSplitFunctionSize
+ // set to something small, and a zero-sized IG (possibly inserted for use by the alignment code). Normally,
+ // the split size will be much larger than the maximum size of an instruction group. The invariant we want
+ // to maintain is that each fragment contains a non-zero amount of code.
+ if (reportCandidate && (candidateSize == 0))
+ {
+#ifdef DEBUG
+ if (EMITVERBOSE)
+ printf("emitSplit: can't split at IG%02u; zero-sized candidate\n", igLastCandidate->igNum);
+#endif
+ reportCandidate = false;
+ }
+
// Report it!
if (reportCandidate)
{
#ifdef DEBUG
- if (EMITVERBOSE && (candidateSize >= maxSplitSize))
- printf("emitSplit: split at IG%02u is size %d, larger than requested maximum size of %d\n",
- igLastCandidate->igNum, candidateSize, maxSplitSize);
+ if (EMITVERBOSE)
+ {
+ printf("emitSplit: split at IG%02u is size %d, %s than requested maximum size of %d\n",
+ igLastCandidate->igNum, candidateSize, (candidateSize >= maxSplitSize) ? "larger" : "less",
+ maxSplitSize);
+ }
#endif
// hand memory ownership to the callback function
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 1dca62569681b0..2e29847aa202b9 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2944,7 +2944,7 @@ void Compiler::fgFindOperOrder()
// and computing lvaOutgoingArgSpaceSize.
//
// Notes:
-// Lowers GT_ARR_LENGTH, GT_ARR_BOUNDS_CHECK, and GT_SIMD_CHK.
+// Lowers GT_ARR_LENGTH, GT_BOUNDS_CHECK.
//
// For target ABIs with fixed out args area, computes upper bound on
// the size of this area from the calls in the IR.
@@ -3007,13 +3007,7 @@ void Compiler::fgSimpleLowering()
break;
}
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
{
// Add in a call to an error routine.
fgSetRngChkTarget(tree, false);
@@ -4483,7 +4477,7 @@ void Compiler::fgSetBlockOrder(BasicBlock* block)
}
break;
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
return Compiler::WALK_ABORT;
default:
diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp
index 4719b4a6e35217..5f6fe110888002 100644
--- a/src/coreclr/jit/gcencode.cpp
+++ b/src/coreclr/jit/gcencode.cpp
@@ -3914,12 +3914,19 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz
{
// Sanity check the offset vs saved patchpoint info.
//
+ const PatchpointInfo* const ppInfo = compiler->info.compPatchpointInfo;
+#if defined(TARGET_AMD64)
// PP info has FP relative offset, to get to caller SP we need to
// subtract off 2 register slots (saved FP, saved RA).
//
- const PatchpointInfo* const ppInfo = compiler->info.compPatchpointInfo;
- const int osrOffset = ppInfo->GenericContextArgOffset() - 2 * REGSIZE_BYTES;
+ const int osrOffset = ppInfo->GenericContextArgOffset() - 2 * REGSIZE_BYTES;
+ assert(offset == osrOffset);
+#elif defined(TARGET_ARM64)
+ // PP info has virtual offset. This is also the caller SP offset.
+ //
+ const int osrOffset = ppInfo->GenericContextArgOffset();
assert(offset == osrOffset);
+#endif
}
#endif
@@ -3950,12 +3957,19 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz
{
// Sanity check the offset vs saved patchpoint info.
//
+ const PatchpointInfo* const ppInfo = compiler->info.compPatchpointInfo;
+#if defined(TARGET_AMD64)
// PP info has FP relative offset, to get to caller SP we need to
// subtract off 2 register slots (saved FP, saved RA).
//
- const PatchpointInfo* const ppInfo = compiler->info.compPatchpointInfo;
- const int osrOffset = ppInfo->KeptAliveThisOffset() - 2 * REGSIZE_BYTES;
+ const int osrOffset = ppInfo->KeptAliveThisOffset() - 2 * REGSIZE_BYTES;
+ assert(offset == osrOffset);
+#elif defined(TARGET_ARM64)
+ // PP info has virtual offset. This is also the caller SP offset.
+ //
+ const int osrOffset = ppInfo->KeptAliveThisOffset();
assert(offset == osrOffset);
+#endif
}
#endif
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 4b14dc38522bef..09dff5c7803f17 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -244,43 +244,36 @@ void GenTree::InitNodeSize()
GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE;
}
- GenTree::s_gtNodeSizes[GT_CALL] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_CAST] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_FTN_ADDR] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_BOX] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_INDEX] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_INDEX_ADDR] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_ARR_BOUNDS_CHECK] = TREE_NODE_SZ_SMALL;
-#ifdef FEATURE_SIMD
- GenTree::s_gtNodeSizes[GT_SIMD_CHK] = TREE_NODE_SZ_SMALL;
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- GenTree::s_gtNodeSizes[GT_HW_INTRINSIC_CHK] = TREE_NODE_SZ_SMALL;
-#endif // FEATURE_HW_INTRINSICS
-
- GenTree::s_gtNodeSizes[GT_ARR_ELEM] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_ARR_INDEX] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_ARR_OFFSET] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_RET_EXPR] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_FIELD] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_CMPXCHG] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_QMARK] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_DYN_BLK] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_STORE_DYN_BLK] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_INTRINSIC] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_ALLOCOBJ] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_CALL] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_CAST] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_FTN_ADDR] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_BOX] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_INDEX] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_INDEX_ADDR] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_BOUNDS_CHECK] = TREE_NODE_SZ_SMALL;
+ GenTree::s_gtNodeSizes[GT_ARR_ELEM] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_ARR_INDEX] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_ARR_OFFSET] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_RET_EXPR] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_FIELD] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_CMPXCHG] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_QMARK] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_DYN_BLK] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_STORE_DYN_BLK] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_INTRINSIC] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_ALLOCOBJ] = TREE_NODE_SZ_LARGE;
#if USE_HELPERS_FOR_INT_DIV
- GenTree::s_gtNodeSizes[GT_DIV] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_UDIV] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_MOD] = TREE_NODE_SZ_LARGE;
- GenTree::s_gtNodeSizes[GT_UMOD] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_DIV] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_UDIV] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_MOD] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_UMOD] = TREE_NODE_SZ_LARGE;
#endif
#ifdef FEATURE_PUT_STRUCT_ARG_STK
// TODO-Throughput: This should not need to be a large node. The object info should be
// obtained from the child node.
- GenTree::s_gtNodeSizes[GT_PUTARG_STK] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_PUTARG_STK] = TREE_NODE_SZ_LARGE;
#if FEATURE_ARG_SPLIT
- GenTree::s_gtNodeSizes[GT_PUTARG_SPLIT] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_PUTARG_SPLIT] = TREE_NODE_SZ_LARGE;
#endif // FEATURE_ARG_SPLIT
#endif // FEATURE_PUT_STRUCT_ARG_STK
@@ -1483,13 +1476,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
return false;
}
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
if (op1->AsBoundsChk()->gtThrowKind != op2->AsBoundsChk()->gtThrowKind)
{
return false;
@@ -1930,13 +1917,7 @@ unsigned Compiler::gtHashValue(GenTree* tree)
hash += static_cast(tree->AsAddrMode()->Offset() << 3) + tree->AsAddrMode()->gtScale;
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
hash = genTreeHashAdd(hash, tree->AsBoundsChk()->gtThrowKind);
break;
@@ -2519,13 +2500,6 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp)
bool canSwap = multiOp->IsReverseOp() ? gtCanSwapOrder(multiOp->Op(2), multiOp->Op(1))
: gtCanSwapOrder(multiOp->Op(1), multiOp->Op(2));
- // The InitN intrinsic for two operands used to be not reversible, so preserve this.
- // TODO-List-Cleanup: delete this only-needed-for-zero-diffs quirk.
- if (multiOp->OperIs(GT_SIMD) && (multiOp->AsSIMD()->GetSIMDIntrinsicId() == SIMDIntrinsicInitN))
- {
- canSwap = false;
- }
-
if (canSwap)
{
if (multiOp->IsReverseOp())
@@ -4057,13 +4031,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif
+ case GT_BOUNDS_CHECK:
costEx = 4; // cmp reg,reg and jae throw (not taken)
costSz = 7; // jump to cold section
break;
@@ -4328,7 +4296,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
// because of trickiness around ensuring the execution order does not change during rationalization.
tryToSwap = false;
}
- else if (GenTree::OperIsBoundsCheck(oper))
+ else if (oper == GT_BOUNDS_CHECK)
{
// Bounds check nodes used to not be binary, thus GTF_REVERSE_OPS was
// not enabled for them. This condition preserves that behavior.
@@ -5401,17 +5369,11 @@ bool GenTree::OperMayThrow(Compiler* comp)
return false;
}
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
case GT_ARR_INDEX:
case GT_ARR_OFFSET:
case GT_LCLHEAP:
case GT_CKFINITE:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
case GT_INDEX_ADDR:
return true;
@@ -7497,16 +7459,10 @@ GenTree* Compiler::gtCloneExpr(
#endif
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
- copy = new (this, oper)
- GenTreeBoundsChk(oper, tree->TypeGet(), tree->AsBoundsChk()->GetIndex(),
- tree->AsBoundsChk()->GetArrayLength(), tree->AsBoundsChk()->gtThrowKind);
+ case GT_BOUNDS_CHECK:
+ copy = new (this, GT_BOUNDS_CHECK)
+ GenTreeBoundsChk(tree->AsBoundsChk()->GetIndex(), tree->AsBoundsChk()->GetArrayLength(),
+ tree->AsBoundsChk()->gtThrowKind);
copy->AsBoundsChk()->gtIndRngFailBB = tree->AsBoundsChk()->gtIndRngFailBB;
break;
@@ -9092,7 +9048,7 @@ void Compiler::gtDispNodeName(GenTree* tree)
}
bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%d)", lea->Offset());
}
- else if (tree->gtOper == GT_ARR_BOUNDS_CHECK)
+ else if (tree->gtOper == GT_BOUNDS_CHECK)
{
switch (tree->AsBoundsChk()->gtThrowKind)
{
@@ -13129,7 +13085,7 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
// This condition exists to preserve previous behavior.
// TODO-CQ: enable folding for bounds checks nodes.
- if (tree->OperIsBoundsCheck())
+ if (tree->OperIs(GT_BOUNDS_CHECK))
{
return tree;
}
@@ -17350,7 +17306,7 @@ void GenTree::ParseArrayAddressWork(Compiler* comp,
case GT_COMMA:
// We don't care about exceptions for this purpose.
- if ((AsOp()->gtOp1->OperGet() == GT_ARR_BOUNDS_CHECK) || AsOp()->gtOp1->IsNothingNode())
+ if (AsOp()->gtOp1->OperIs(GT_BOUNDS_CHECK) || AsOp()->gtOp1->IsNothingNode())
{
AsOp()->gtOp2->ParseArrayAddressWork(comp, inputMul, pArr, pInxVN, pOffset, pFldSeq);
return;
@@ -21725,3 +21681,26 @@ bool GenTree::IsInvariant() const
GenTree* lclVarTree = nullptr;
return OperIsConst() || Compiler::impIsAddressInLocal(this, &lclVarTree);
}
+
+//------------------------------------------------------------------------
+// IsNeverNegative: returns true if the given tree is known to be never
+// negative, i. e. the upper bit will always be zero.
+// Only valid for integral types.
+//
+// Arguments:
+// comp - Compiler object, needed for IntegralRange::ForNode
+//
+// Return Value:
+// true if the given tree is known to be never negative
+//
+bool GenTree::IsNeverNegative(Compiler* comp) const
+{
+ assert(varTypeIsIntegral(this));
+
+ if (IsIntegralConst())
+ {
+ return AsIntConCommon()->IntegralValue() >= 0;
+ }
+ // TODO-Casts: extend IntegralRange to handle constants
+ return IntegralRange::ForNode((GenTree*)this, comp).IsPositive();
+}
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 3fd3792eb8b1f9..53bba64997b8ee 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -579,7 +579,7 @@ enum GenTreeFlags : unsigned int
GTF_DIV_BY_CNS_OPT = 0x80000000, // GT_DIV -- Uses the division by constant optimization to compute this division
- GTF_ARR_BOUND_INBND = 0x80000000, // GT_ARR_BOUNDS_CHECK -- have proved this check is always in-bounds
+ GTF_CHK_INDEX_INBND = 0x80000000, // GT_BOUNDS_CHECK -- have proved this check is always in-bounds
GTF_ARRLEN_ARR_IDX = 0x80000000, // GT_ARR_LENGTH -- Length which feeds into an array index expression
GTF_ARRLEN_NONFAULTING = 0x20000000, // GT_ARR_LENGTH -- An array length operation that cannot fault. Same as GT_IND_NONFAULTING.
@@ -1685,32 +1685,6 @@ struct GenTree
return (gtOper == GT_JTRUE) || (gtOper == GT_JCMP) || (gtOper == GT_JCC);
}
- static bool OperIsBoundsCheck(genTreeOps op)
- {
- if (op == GT_ARR_BOUNDS_CHECK)
- {
- return true;
- }
-#ifdef FEATURE_SIMD
- if (op == GT_SIMD_CHK)
- {
- return true;
- }
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- if (op == GT_HW_INTRINSIC_CHK)
- {
- return true;
- }
-#endif // FEATURE_HW_INTRINSICS
- return false;
- }
-
- bool OperIsBoundsCheck() const
- {
- return OperIsBoundsCheck(OperGet());
- }
-
#ifdef DEBUG
bool NullOp1Legal() const
{
@@ -2286,6 +2260,8 @@ struct GenTree
bool IsInvariant() const;
+ bool IsNeverNegative(Compiler* comp) const;
+
bool IsReuseRegVal() const
{
// This can be extended to non-constant nodes, but not to local or indir nodes.
@@ -5739,19 +5715,19 @@ struct GenTreeArrLen : public GenTreeUnOp
};
// This takes:
-// - a comparison value (generally an array length),
+// - a length value
// - an index value, and
// - the label to jump to if the index is out of range.
// - the "kind" of the throw block to branch to on failure
// It generates no result.
-
+//
struct GenTreeBoundsChk : public GenTreeOp
{
- BasicBlock* gtIndRngFailBB; // Basic block to jump to for array-index-out-of-range
+ BasicBlock* gtIndRngFailBB; // Basic block to jump to for index-out-of-range
SpecialCodeKind gtThrowKind; // Kind of throw block to branch to on failure
- GenTreeBoundsChk(genTreeOps oper, var_types type, GenTree* index, GenTree* arrLen, SpecialCodeKind kind)
- : GenTreeOp(oper, type, index, arrLen), gtIndRngFailBB(nullptr), gtThrowKind(kind)
+ GenTreeBoundsChk(GenTree* index, GenTree* length, SpecialCodeKind kind)
+ : GenTreeOp(GT_BOUNDS_CHECK, TYP_VOID, index, length), gtIndRngFailBB(nullptr), gtThrowKind(kind)
{
gtFlags |= GTF_EXCEPT;
}
@@ -5762,19 +5738,19 @@ struct GenTreeBoundsChk : public GenTreeOp
#endif
// If this check is against GT_ARR_LENGTH, returns array reference, else "NULL".
- GenTree* GetArray()
+ GenTree* GetArray() const
{
return GetArrayLength()->OperIs(GT_ARR_LENGTH) ? GetArrayLength()->AsArrLen()->ArrRef() : nullptr;
}
// The index expression.
- GenTree* GetIndex()
+ GenTree* GetIndex() const
{
return gtOp1;
}
- // An expression for the length of the array being indexed.
- GenTree* GetArrayLength()
+ // An expression for the length.
+ GenTree* GetArrayLength() const
{
return gtOp2;
}
diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h
index 6f6462d6743bd0..0fec37b70b2a7c 100644
--- a/src/coreclr/jit/gtlist.h
+++ b/src/coreclr/jit/gtlist.h
@@ -77,7 +77,7 @@ GTNODE(ADDR , GenTreeOp ,0,GTK_UNOP) // addre
GTNODE(IND , GenTreeIndir ,0,GTK_UNOP) // load indirection
GTNODE(STOREIND , GenTreeStoreInd ,0,(GTK_BINOP|GTK_NOVALUE)) // store indirection
-GTNODE(ARR_BOUNDS_CHECK , GenTreeBoundsChk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE))// array bounds check
+GTNODE(BOUNDS_CHECK , GenTreeBoundsChk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // a bounds check - for arrays/spans/SIMDs/HWINTRINSICs
GTNODE(OBJ , GenTreeObj ,0,(GTK_UNOP|GTK_EXOP)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
GTNODE(STORE_OBJ , GenTreeObj ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
@@ -88,20 +88,7 @@ GTNODE(STORE_DYN_BLK , GenTreeDynBlk ,0,(GTK_SPECIAL|GTK_NOVALUE)) // Dy
GTNODE(BOX , GenTreeBox ,0,(GTK_UNOP|GTK_EXOP|GTK_NOTLIR))
GTNODE(FIELD , GenTreeField ,0,(GTK_UNOP|GTK_EXOP)) // Member-field
-
-#ifdef FEATURE_SIMD
-GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE))// Compare whether an index is less than the given SIMD vector length, and call CORINFO_HELP_RNGCHKFAIL if not.
- // TODO-CQ: In future may want to add a field that specifies different exceptions but we'll
- // need VM assistance for that.
- // TODO-CQ: It would actually be very nice to make this an unconditional throw, and expose the control flow that
- // does the compare, so that it can be more easily optimized. But that involves generating qmarks at import time...
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HW_INTRINSIC_CHK , GenTreeBoundsChk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE))// Compare whether an imm8 argument is in the valid range, and throw ArgumentOutOfRangeException if not.
-#endif
-
-GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,(GTK_UNOP|GTK_EXOP)) // object allocator
+GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,(GTK_UNOP|GTK_EXOP)) // object allocator
GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk
diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h
index 70a7901b7aca2a..591753837fe892 100644
--- a/src/coreclr/jit/gtstructs.h
+++ b/src/coreclr/jit/gtstructs.h
@@ -74,17 +74,13 @@ GTSTRUCT_1(Intrinsic , GT_INTRINSIC)
GTSTRUCT_1(Index , GT_INDEX)
GTSTRUCT_1(IndexAddr , GT_INDEX_ADDR)
#if defined(FEATURE_HW_INTRINSICS) && defined(FEATURE_SIMD)
-GTSTRUCT_3(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK, GT_HW_INTRINSIC_CHK)
GTSTRUCT_N(MultiOp , GT_SIMD, GT_HWINTRINSIC)
#elif defined(FEATURE_SIMD)
-GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK)
GTSTRUCT_N(MultiOp , GT_SIMD)
#elif defined(FEATURE_HW_INTRINSICS)
-GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_HW_INTRINSIC_CHK)
GTSTRUCT_N(MultiOp , GT_HWINTRINSIC)
-#else // !FEATURE_SIMD && !FEATURE_HW_INTRINSICS
-GTSTRUCT_1(BoundsChk , GT_ARR_BOUNDS_CHECK)
-#endif // !FEATURE_SIMD && !FEATURE_HW_INTRINSICS
+#endif
+GTSTRUCT_1(BoundsChk , GT_BOUNDS_CHECK)
GTSTRUCT_1(ArrLen , GT_ARR_LENGTH)
GTSTRUCT_1(ArrElem , GT_ARR_ELEM)
GTSTRUCT_1(ArrOffs , GT_ARR_OFFSET)
diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp
index f4ce9bd171ee1b..ad86d58613dcd3 100644
--- a/src/coreclr/jit/hwintrinsic.cpp
+++ b/src/coreclr/jit/hwintrinsic.cpp
@@ -462,7 +462,7 @@ GenTree* Compiler::getArgForHWIntrinsic(var_types argType,
}
//------------------------------------------------------------------------
-// addRangeCheckIfNeeded: add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic
+// addRangeCheckIfNeeded: add a GT_BOUNDS_CHECK node for non-full-range imm-intrinsic
//
// Arguments:
// intrinsic -- intrinsic ID
@@ -472,7 +472,7 @@ GenTree* Compiler::getArgForHWIntrinsic(var_types argType,
// immUpperBound -- upper incl. bound for a value of the immediate operand (for a non-full-range imm-intrinsic)
//
// Return Value:
-// add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
+// add a GT_BOUNDS_CHECK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
// when the imm-argument is not in the valid range
//
GenTree* Compiler::addRangeCheckIfNeeded(
@@ -501,7 +501,7 @@ GenTree* Compiler::addRangeCheckIfNeeded(
}
//------------------------------------------------------------------------
-// addRangeCheckForHWIntrinsic: add a GT_HW_INTRINSIC_CHK node for an intrinsic
+// addRangeCheckForHWIntrinsic: add a GT_BOUNDS_CHECK node for an intrinsic
//
// Arguments:
// immOp -- the immediate operand of the intrinsic
@@ -509,7 +509,7 @@ GenTree* Compiler::addRangeCheckIfNeeded(
// immUpperBound -- upper incl. bound for a value of the immediate operand (for a non-full-range imm-intrinsic)
//
// Return Value:
-// add a GT_HW_INTRINSIC_CHK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
+// add a GT_BOUNDS_CHECK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
// when the imm-argument is not in the valid range
//
GenTree* Compiler::addRangeCheckForHWIntrinsic(GenTree* immOp, int immLowerBound, int immUpperBound)
@@ -539,8 +539,8 @@ GenTree* Compiler::addRangeCheckForHWIntrinsic(GenTree* immOp, int immLowerBound
immOpDup = gtNewOperNode(GT_SUB, TYP_INT, immOpDup, gtNewIconNode(immLowerBound, TYP_INT));
}
- GenTreeBoundsChk* hwIntrinsicChk = new (this, GT_HW_INTRINSIC_CHK)
- GenTreeBoundsChk(GT_HW_INTRINSIC_CHK, TYP_VOID, immOpDup, adjustedUpperBoundNode, SCK_ARG_RNG_EXCPN);
+ GenTreeBoundsChk* hwIntrinsicChk =
+ new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(immOpDup, adjustedUpperBoundNode, SCK_ARG_RNG_EXCPN);
return gtNewOperNode(GT_COMMA, immOp->TypeGet(), hwIntrinsicChk, immOp);
}
diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
index 2bbe2835816fcf..cd9e0f8f8fc3d5 100644
--- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
+++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
@@ -1047,7 +1047,7 @@ void CodeGen::genHWIntrinsic_R_R_R_RM(
// Note:
// This function can be used for all imm-intrinsics (whether full-range or not),
// The compiler front-end (i.e. importer) is responsible to insert a range-check IR
-// (GT_HW_INTRINSIC_CHK) for imm8 argument, so this function does not need to do range-check.
+// (GT_BOUNDS_CHECK) for imm8 argument, so this function does not need to do range-check.
//
template
void CodeGen::genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsic,
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 3519ac6f7b6505..57c3604899b7c5 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -3364,7 +3364,7 @@ GenTree* Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig)
#endif
)
{
- if (newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEW_MDARR_NONVARARG))
+ if (newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEW_MDARR))
{
return nullptr;
}
@@ -4094,8 +4094,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
CORINFO_FIELD_HANDLE lengthHnd = info.compCompHnd->getFieldInClass(clsHnd, 1);
const unsigned lengthOffset = info.compCompHnd->getFieldOffset(lengthHnd);
GenTree* length = gtNewFieldRef(TYP_INT, lengthHnd, ptrToSpan, lengthOffset);
- GenTree* boundsCheck = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, length, SCK_RNGCHK_FAIL);
+ GenTree* boundsCheck = new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(index, length, SCK_RNGCHK_FAIL);
// Element access
index = indexClone;
@@ -7428,107 +7427,59 @@ void Compiler::impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI
GenTree* node;
- //
- // There are two different JIT helpers that can be used to allocate
- // multi-dimensional arrays:
- //
- // - CORINFO_HELP_NEW_MDARR - takes the array dimensions as varargs.
- // This variant is deprecated. It should be eventually removed.
- //
- // - CORINFO_HELP_NEW_MDARR_NONVARARG - takes the array dimensions as
- // pointer to block of int32s. This variant is more portable.
- //
- // The non-varargs helper is enabled for CoreRT only for now. Enabling this
- // unconditionally would require ReadyToRun version bump.
- //
- CLANG_FORMAT_COMMENT_ANCHOR;
-
- if (!opts.IsReadyToRun() || IsTargetAbi(CORINFO_CORERT_ABI))
+ // Reuse the temp used to pass the array dimensions to avoid bloating
+ // the stack frame in case there are multiple calls to multi-dim array
+ // constructors within a single method.
+ if (lvaNewObjArrayArgs == BAD_VAR_NUM)
{
+ lvaNewObjArrayArgs = lvaGrabTemp(false DEBUGARG("NewObjArrayArgs"));
+ lvaTable[lvaNewObjArrayArgs].lvType = TYP_BLK;
+ lvaTable[lvaNewObjArrayArgs].lvExactSize = 0;
+ }
- // Reuse the temp used to pass the array dimensions to avoid bloating
- // the stack frame in case there are multiple calls to multi-dim array
- // constructors within a single method.
- if (lvaNewObjArrayArgs == BAD_VAR_NUM)
- {
- lvaNewObjArrayArgs = lvaGrabTemp(false DEBUGARG("NewObjArrayArgs"));
- lvaTable[lvaNewObjArrayArgs].lvType = TYP_BLK;
- lvaTable[lvaNewObjArrayArgs].lvExactSize = 0;
- }
-
- // Increase size of lvaNewObjArrayArgs to be the largest size needed to hold 'numArgs' integers
- // for our call to CORINFO_HELP_NEW_MDARR_NONVARARG.
- lvaTable[lvaNewObjArrayArgs].lvExactSize =
- max(lvaTable[lvaNewObjArrayArgs].lvExactSize, pCallInfo->sig.numArgs * sizeof(INT32));
-
- // The side-effects may include allocation of more multi-dimensional arrays. Spill all side-effects
- // to ensure that the shared lvaNewObjArrayArgs local variable is only ever used to pass arguments
- // to one allocation at a time.
- impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportNewObjArray"));
-
- //
- // The arguments of the CORINFO_HELP_NEW_MDARR_NONVARARG helper are:
- // - Array class handle
- // - Number of dimension arguments
- // - Pointer to block of int32 dimensions - address of lvaNewObjArrayArgs temp.
- //
-
- node = gtNewLclvNode(lvaNewObjArrayArgs, TYP_BLK);
- node = gtNewOperNode(GT_ADDR, TYP_I_IMPL, node);
-
- // Pop dimension arguments from the stack one at a time and store it
- // into lvaNewObjArrayArgs temp.
- for (int i = pCallInfo->sig.numArgs - 1; i >= 0; i--)
- {
- GenTree* arg = impImplicitIorI4Cast(impPopStack().val, TYP_INT);
-
- GenTree* dest = gtNewLclvNode(lvaNewObjArrayArgs, TYP_BLK);
- dest = gtNewOperNode(GT_ADDR, TYP_I_IMPL, dest);
- dest = gtNewOperNode(GT_ADD, TYP_I_IMPL, dest,
- new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, sizeof(INT32) * i));
- dest = gtNewOperNode(GT_IND, TYP_INT, dest);
-
- node = gtNewOperNode(GT_COMMA, node->TypeGet(), gtNewAssignNode(dest, arg), node);
- }
+ // Increase size of lvaNewObjArrayArgs to be the largest size needed to hold 'numArgs' integers
+ // for our call to CORINFO_HELP_NEW_MDARR.
+ lvaTable[lvaNewObjArrayArgs].lvExactSize =
+ max(lvaTable[lvaNewObjArrayArgs].lvExactSize, pCallInfo->sig.numArgs * sizeof(INT32));
- GenTreeCall::Use* args = gtNewCallArgs(node);
+ // The side-effects may include allocation of more multi-dimensional arrays. Spill all side-effects
+ // to ensure that the shared lvaNewObjArrayArgs local variable is only ever used to pass arguments
+ // to one allocation at a time.
+ impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportNewObjArray"));
- // pass number of arguments to the helper
- args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args);
+ //
+ // The arguments of the CORINFO_HELP_NEW_MDARR helper are:
+ // - Array class handle
+ // - Number of dimension arguments
+ // - Pointer to block of int32 dimensions - address of lvaNewObjArrayArgs temp.
+ //
- args = gtPrependNewCallArg(classHandle, args);
+ node = gtNewLclvNode(lvaNewObjArrayArgs, TYP_BLK);
+ node = gtNewOperNode(GT_ADDR, TYP_I_IMPL, node);
- node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR_NONVARARG, TYP_REF, args);
- }
- else
+ // Pop dimension arguments from the stack one at a time and store it
+ // into lvaNewObjArrayArgs temp.
+ for (int i = pCallInfo->sig.numArgs - 1; i >= 0; i--)
{
- //
- // The varargs helper needs the type and method handles as last
- // and last-1 param (this is a cdecl call, so args will be
- // pushed in reverse order on the CPU stack)
- //
+ GenTree* arg = impImplicitIorI4Cast(impPopStack().val, TYP_INT);
- GenTreeCall::Use* args = gtNewCallArgs(classHandle);
+ GenTree* dest = gtNewLclvNode(lvaNewObjArrayArgs, TYP_BLK);
+ dest = gtNewOperNode(GT_ADDR, TYP_I_IMPL, dest);
+ dest = gtNewOperNode(GT_ADD, TYP_I_IMPL, dest,
+ new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, sizeof(INT32) * i));
+ dest = gtNewOperNode(GT_IND, TYP_INT, dest);
- // pass number of arguments to the helper
- args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args);
+ node = gtNewOperNode(GT_COMMA, node->TypeGet(), gtNewAssignNode(dest, arg), node);
+ }
- args = impPopCallArgs(pCallInfo->sig.numArgs, &pCallInfo->sig, args);
+ GenTreeCall::Use* args = gtNewCallArgs(node);
- node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, args);
+ // pass number of arguments to the helper
+ args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args);
- // varargs, so we pop the arguments
- node->gtFlags |= GTF_CALL_POP_ARGS;
+ args = gtPrependNewCallArg(classHandle, args);
-#ifdef DEBUG
- // At the present time we don't track Caller pop arguments
- // that have GC references in them
- for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
- {
- assert(use.GetNode()->TypeGet() != TYP_REF);
- }
-#endif
- }
+ node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, args);
for (GenTreeCall::Use& use : node->AsCall()->Args())
{
@@ -14932,18 +14883,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// At present this can only be String
else if (clsFlags & CORINFO_FLG_VAROBJSIZE)
{
- if (IsTargetAbi(CORINFO_CORERT_ABI))
- {
- // The dummy argument does not exist in CoreRT
- newObjThisPtr = nullptr;
- }
- else
- {
- // This is the case for variable-sized objects that are not
- // arrays. In this case, call the constructor with a null 'this'
- // pointer
- newObjThisPtr = gtNewIconNode(0, TYP_REF);
- }
+ // Skip this thisPtr argument
+ newObjThisPtr = nullptr;
/* Remember that this basic block contains 'new' of an object */
block->bbFlags |= BBF_HAS_NEWOBJ;
@@ -20838,33 +20779,8 @@ GenTree* Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, In
// TODO-1stClassStructs: We currently do not reuse an existing lclVar
// if it is a struct, because it requires some additional handling.
- bool substitute = false;
- switch (argNode->OperGet())
- {
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
- {
- // Enable for all parameterless (=invariant) hw intrinsics such as
- // Vector128<>.Zero and Vector256<>.AllBitSets. We might consider
- // doing that for Vector.Create(cns) as well.
- if (argNode->AsHWIntrinsic()->GetOperandCount() == 0)
- {
- substitute = true;
- }
- break;
- }
-#endif
-
- // TODO: Enable substitution for CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE (typeof(T))
- // but in order to benefit from that, we need to move various "typeof + IsValueType"
- // optimizations from importer to morph.
-
- default:
- break;
- }
-
- if (substitute || (!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef &&
- !argInfo.argHasCallerLocalRef))
+ if ((!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef &&
+ !argInfo.argHasCallerLocalRef))
{
/* Get a *LARGE* LCL_VAR node */
op1 = gtNewLclLNode(tmpNum, genActualType(lclTyp) DEBUGARG(lclNum));
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 5f9fa62008b304..73b71a07a8c492 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -522,6 +522,10 @@ CONFIG_INTEGER(TC_OnStackReplacement_InitialCounter, W("TC_OnStackReplacement_In
// Enable partial compilation for Tier0 methods
CONFIG_INTEGER(TC_PartialCompilation, W("TC_PartialCompilation"), 0)
+#if defined(DEBUG)
+CONFIG_STRING(JitEnableOsrRange, W("JitEnableOsrRange")) // Enable osr for only some methods
+#endif // debug
+
// Profile instrumentation options
CONFIG_INTEGER(JitMinimalJitProfiling, W("JitMinimalJitProfiling"), 1)
CONFIG_INTEGER(JitMinimalPrejitProfiling, W("JitMinimalPrejitProfiling"), 0)
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 4d01e63ef13b6c..c5a30e890adb66 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -5182,11 +5182,12 @@ void Compiler::lvaFixVirtualFrameOffsets()
assert(!varDsc->lvMustInit); // It is never "must init".
varDsc->SetStackOffset(codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar));
- // With OSR the new frame RBP points at the base of the new frame, but the virtual offsets
- // are from the base of the old frame. Adjust.
if (opts.IsOSR())
{
- varDsc->SetStackOffset(varDsc->GetStackOffset() - info.compPatchpointInfo->FpToSpDelta());
+ // With OSR RBP points at the base of the OSR frame, but the virtual offsets
+ // are from the base of the Tier0 frame. Adjust.
+ //
+ varDsc->SetStackOffset(varDsc->GetStackOffset() - info.compPatchpointInfo->TotalFrameSize());
}
}
#endif
@@ -5221,17 +5222,19 @@ void Compiler::lvaFixVirtualFrameOffsets()
else
{
// FP is used.
- JITDUMP("--- delta bump %d for RBP frame\n", codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta());
+ JITDUMP("--- delta bump %d for FP frame\n", codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta());
delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta();
}
#endif // TARGET_AMD64
- // For OSR, update the delta to reflect the current policy that
- // RBP points at the base of the new frame, and RSP is relative to that RBP.
if (opts.IsOSR())
{
- JITDUMP("--- delta bump %d for OSR\n", info.compPatchpointInfo->FpToSpDelta());
- delta += info.compPatchpointInfo->FpToSpDelta();
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
+ // Stack offset includes Tier0 frame.
+ //
+ JITDUMP("--- delta bump %d for OSR + Tier0 frame\n", info.compPatchpointInfo->TotalFrameSize());
+ delta += info.compPatchpointInfo->TotalFrameSize();
+#endif
}
JITDUMP("--- virtual stack offset to actual stack offset delta is %d\n", delta);
@@ -6056,16 +6059,18 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
{
lvaTable[lvaRetAddrVar].SetStackOffset(stkOffs);
}
+#endif
- // If we are an OSR method, we "inherit" the frame of the original method,
- // and the stack is already double aligned on entry (since the return address push
- // and any special alignment push happened "before").
+ // If we are an OSR method, we "inherit" the frame of the original method
+ //
if (opts.IsOSR())
{
- originalFrameSize = info.compPatchpointInfo->FpToSpDelta();
+ originalFrameSize = info.compPatchpointInfo->TotalFrameSize();
originalFrameStkOffs = stkOffs;
stkOffs -= originalFrameSize;
}
+
+#ifdef TARGET_XARCH
// TODO-AMD64-CQ: for X64 eventually this should be pushed with all the other
// calleeregs. When you fix this, you'll also need to fix
// the assert at the bottom of this method
@@ -6073,7 +6078,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
{
stkOffs -= REGSIZE_BYTES;
}
-#endif // TARGET_XARCH
+#endif
int preSpillSize = 0;
bool mustDoubleAlign = false;
@@ -6234,9 +6239,10 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
int originalOffset = info.compPatchpointInfo->MonitorAcquiredOffset();
int offset = originalFrameStkOffs + originalOffset;
- JITDUMP("---OSR--- V%02u (on old frame, monitor aquired) old rbp offset %d old frame offset %d new "
- "virt offset %d\n",
- lvaMonAcquired, originalOffset, originalFrameStkOffs, offset);
+ JITDUMP(
+ "---OSR--- V%02u (on tier0 frame, monitor aquired) tier0 FP-rel offset %d tier0 frame offset %d new "
+ "virt offset %d\n",
+ lvaMonAcquired, originalOffset, originalFrameStkOffs, offset);
lvaTable[lvaMonAcquired].SetStackOffset(offset);
}
@@ -6468,7 +6474,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
int originalOffset = info.compPatchpointInfo->Offset(lclNum);
int offset = originalFrameStkOffs + originalOffset;
- JITDUMP("---OSR--- V%02u (on old frame) old rbp offset %d old frame offset %d new virt offset %d\n",
+ JITDUMP("---OSR--- V%02u (on tier0 frame) tier0 FP-rel offset %d tier0 frame offset %d new virt offset "
+ "%d\n",
lclNum, originalOffset, originalFrameStkOffs, offset);
lvaTable[lclNum].SetStackOffset(offset);
@@ -6503,7 +6510,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
int originalOffset = info.compPatchpointInfo->SecurityCookieOffset();
int offset = originalFrameStkOffs + originalOffset;
- JITDUMP("---OSR--- V%02u (on old frame, security cookie) old rbp offset %d old frame offset %d new "
+ JITDUMP("---OSR--- V%02u (on tier0 frame, security cookie) tier0 FP-rel offset %d tier0 frame "
+ "offset %d new "
"virt offset %d\n",
lclNum, originalOffset, originalFrameStkOffs, offset);
@@ -7471,6 +7479,10 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r
{
printf(" single-def");
}
+ if (lvaIsOSRLocal(lclNum) && varDsc->lvOnFrame)
+ {
+ printf(" tier0-frame");
+ }
#ifndef TARGET_64BIT
if (varDsc->lvStructDoubleAlign)
@@ -7780,23 +7792,30 @@ int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased, bool forRo
offset += codeGen->genCallerSPtoInitialSPdelta();
}
-#ifdef TARGET_AMD64
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
if (forRootFrame && opts.IsOSR())
{
+ const PatchpointInfo* const ppInfo = info.compPatchpointInfo;
+
+#if defined(TARGET_AMD64)
// The offset computed above already includes the OSR frame adjustment, plus the
// pop of the "pseudo return address" from the OSR frame.
//
- // To get to root method caller-SP, we need to subtract off the original frame
- // size and the pushed return address and RBP for that frame (which we know is an
+ // To get to root method caller-SP, we need to subtract off the tier0 frame
+ // size and the pushed return address and RBP for the tier0 frame (which we know is an
// RPB frame).
//
- // ppInfo's FpToSpDelta also accounts for the popped pseudo return address
- // between the original method frame and the OSR frame. So the net adjustment
- // is simply FpToSpDelta plus one register.
+ // ppInfo's TotalFrameSize also accounts for the popped pseudo return address
+ // between the tier0 method frame and the OSR frame. So the net adjustment
+ // is simply TotalFrameSize plus one register.
//
+ const int adjustment = ppInfo->TotalFrameSize() + REGSIZE_BYTES;
+
+#elif defined(TARGET_ARM64)
+
+ const int adjustment = ppInfo->TotalFrameSize();
+#endif
- const PatchpointInfo* const ppInfo = info.compPatchpointInfo;
- const int adjustment = ppInfo->FpToSpDelta() + REGSIZE_BYTES;
offset -= adjustment;
}
#else
diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp
index a0134f214f0f35..4f8d9334249cf0 100644
--- a/src/coreclr/jit/liveness.cpp
+++ b/src/coreclr/jit/liveness.cpp
@@ -2042,16 +2042,10 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR
case GT_MEMORYBARRIER:
case GT_JMP:
case GT_STOREIND:
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
case GT_STORE_OBJ:
case GT_STORE_BLK:
case GT_STORE_DYN_BLK:
-#if defined(FEATURE_SIMD)
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
case GT_JCMP:
case GT_CMP:
case GT_JCC:
diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp
index 82847b99814668..b97bc84c6cebf6 100644
--- a/src/coreclr/jit/loopcloning.cpp
+++ b/src/coreclr/jit/loopcloning.cpp
@@ -1447,7 +1447,7 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext*
}
#endif // DEBUG
- if (bndsChkNode->gtGetOp1()->OperIsBoundsCheck())
+ if (bndsChkNode->gtGetOp1()->OperIs(GT_BOUNDS_CHECK))
{
// This COMMA node will only represent a bounds check if we've haven't already removed this
// bounds check in some other nesting cloned loop. For example, consider:
@@ -2174,7 +2174,7 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum)
// Example tree to pattern match:
//
// * COMMA int
-// +--* ARR_BOUNDS_CHECK_Rng void
+// +--* BOUNDS_CHECK_Rng void
// | +--* LCL_VAR int V02 loc1
// | \--* ARR_LENGTH int
// | \--* LCL_VAR ref V00 arg0
@@ -2191,7 +2191,7 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum)
// Note that byte arrays don't require the LSH to scale the index, so look like this:
//
// * COMMA ubyte
-// +--* ARR_BOUNDS_CHECK_Rng void
+// +--* BOUNDS_CHECK_Rng void
// | +--* LCL_VAR int V03 loc2
// | \--* ARR_LENGTH int
// | \--* LCL_VAR ref V00 arg0
@@ -2216,7 +2216,7 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN
return false;
}
GenTree* before = tree->gtGetOp1();
- if (before->gtOper != GT_ARR_BOUNDS_CHECK)
+ if (!before->OperIs(GT_BOUNDS_CHECK))
{
return false;
}
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 2467a00accdaa7..ae5a41c52e6827 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -198,13 +198,7 @@ GenTree* Lowering::LowerNode(GenTree* node)
break;
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
ContainCheckBoundsChk(node->AsBoundsChk());
break;
#endif // TARGET_XARCH
diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp
index 279a20e7a36213..ae02e5e3a9928b 100644
--- a/src/coreclr/jit/lowerarmarch.cpp
+++ b/src/coreclr/jit/lowerarmarch.cpp
@@ -104,13 +104,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const
case GT_LE:
case GT_GE:
case GT_GT:
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif
+ case GT_BOUNDS_CHECK:
return emitter::emitIns_valid_imm_for_cmp(immVal, size);
case GT_AND:
case GT_OR:
@@ -1722,7 +1716,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
//
void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
{
- assert(node->OperIsBoundsCheck());
+ assert(node->OperIs(GT_BOUNDS_CHECK));
if (!CheckImmedAndMakeContained(node, node->GetIndex()))
{
CheckImmedAndMakeContained(node, node->GetArrayLength());
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 5ff14224d57bd1..c8076c4525f2df 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -5029,7 +5029,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node)
//
void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
{
- assert(node->OperIsBoundsCheck());
+ assert(node->OperIs(GT_BOUNDS_CHECK));
GenTree* other;
if (CheckImmedAndMakeContained(node, node->GetIndex()))
{
diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp
index fb5ad16006dad7..9050f1f5ac7d2d 100644
--- a/src/coreclr/jit/lsraarm.cpp
+++ b/src/coreclr/jit/lsraarm.cpp
@@ -509,18 +509,13 @@ int LinearScan::BuildNode(GenTree* tree)
}
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
- {
+ case GT_BOUNDS_CHECK:
// Consumes arrLen & index - has no result
srcCount = 2;
assert(dstCount == 0);
BuildUse(tree->AsBoundsChk()->GetIndex());
BuildUse(tree->AsBoundsChk()->GetArrayLength());
- }
- break;
+ break;
case GT_ARR_ELEM:
// These must have been lowered to GT_ARR_INDEX
diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp
index 1b201603f95a1b..0f4ad4cdb04dd3 100644
--- a/src/coreclr/jit/lsraarm64.cpp
+++ b/src/coreclr/jit/lsraarm64.cpp
@@ -614,13 +614,7 @@ int LinearScan::BuildNode(GenTree* tree)
}
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
{
GenTreeBoundsChk* node = tree->AsBoundsChk();
// Consumes arrLen & index - has no result
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index fb5747079fa90b..adf2a3be3f16b2 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -3465,8 +3465,12 @@ int LinearScan::BuildStoreLoc(GenTreeLclVarCommon* storeLoc)
{
// Need an additional register to create a SIMD8 from EAX/EDX without SSE4.1.
buildInternalFloatRegisterDefForNode(storeLoc, allSIMDRegs());
- // This internal register must be different from the target register.
- setInternalRegsDelayFree = true;
+
+ if (isCandidateVar(varDsc))
+ {
+ // This internal register must be different from the target register.
+ setInternalRegsDelayFree = true;
+ }
}
}
#endif // FEATURE_SIMD && TARGET_X86
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 4705ee6798595e..ea4c86639d5ec5 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -524,14 +524,7 @@ int LinearScan::BuildNode(GenTree* tree)
srcCount = BuildLclHeap(tree);
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
-
+ case GT_BOUNDS_CHECK:
// Consumes arrLen & index - has no result
assert(dstCount == 0);
srcCount = BuildOperandUses(tree->AsBoundsChk()->GetIndex());
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index bbae7b6a347df2..0729d06e414f13 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -5111,7 +5111,7 @@ void Compiler::fgMoveOpsLeft(GenTree* tree)
void Compiler::fgSetRngChkTarget(GenTree* tree, bool delay)
{
- if (tree->OperIsBoundsCheck())
+ if (tree->OperIs(GT_BOUNDS_CHECK))
{
GenTreeBoundsChk* const boundsChk = tree->AsBoundsChk();
BasicBlock* const failBlock = fgSetRngChkTargetInner(boundsChk->gtThrowKind, delay);
@@ -5162,7 +5162,7 @@ BasicBlock* Compiler::fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay)
* The orginal GT_INDEX node is bashed into the GT_IND node that accesses
* the array element. We expand the GT_INDEX node into a larger tree that
* evaluates the array base and index. The simplest expansion is a GT_COMMA
- * with a GT_ARR_BOUNDS_CHECK and a GT_IND with a GTF_INX_RNGCHK flag.
+ * with a GT_BOUNDS_CHECK and a GT_IND with a GTF_INX_RNGCHK flag.
* For complex array or index expressions one or more GT_COMMA assignments
* are inserted so that we only evaluate the array or index expressions once.
*
@@ -5252,7 +5252,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
// side-effecting.
// 2. Evaluate the array index expression and store the result in a temp if the expression is complex or
// side-effecting.
- // 3. Perform an explicit bounds check: GT_ARR_BOUNDS_CHECK(index, GT_ARR_LENGTH(array))
+ // 3. Perform an explicit bounds check: GT_BOUNDS_CHECK(index, GT_ARR_LENGTH(array))
// 4. Compute the address of the element that will be accessed:
// GT_ADD(GT_ADD(array, firstElementOffset), GT_MUL(index, elementSize))
// 5. Dereference the address with a GT_IND.
@@ -5311,7 +5311,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
GenTree* indexDefn = nullptr; // non-NULL if we need to allocate a temp for the index expression
GenTree* bndsChk = nullptr;
- // If we're doing range checking, introduce a GT_ARR_BOUNDS_CHECK node for the address.
+ // If we're doing range checking, introduce a GT_BOUNDS_CHECK node for the address.
if (chkd)
{
GenTree* arrRef2 = nullptr; // The second copy will be used in array address expression
@@ -5360,7 +5360,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
noway_assert(index2 != nullptr);
}
- // Next introduce a GT_ARR_BOUNDS_CHECK node
+ // Next introduce a GT_BOUNDS_CHECK node
var_types bndsChkType = TYP_INT; // By default, try to use 32-bit comparison for array bounds check.
#ifdef TARGET_64BIT
@@ -5380,8 +5380,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
arrLen = gtNewCastNode(bndsChkType, arrLen, true, bndsChkType);
}
- GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, arrLen, SCK_RNGCHK_FAIL);
+ GenTreeBoundsChk* arrBndsChk = new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(index, arrLen, SCK_RNGCHK_FAIL);
bndsChk = arrBndsChk;
@@ -11160,13 +11159,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
}
}
- // array.Length is always positive so GT_DIV can be changed to GT_UDIV
- // if op2 is a positive cns
- if (!optValnumCSE_phase && op1->OperIs(GT_ARR_LENGTH) && op2->IsIntegralConst() &&
- op2->AsIntCon()->IconValue() >= 2) // for 0 and 1 it doesn't matter if it's UDIV or DIV
+ // Convert DIV to UDIV if boths op1 and op2 are known to be never negative
+ if (!gtIsActiveCSE_Candidate(tree) && varTypeIsIntegral(tree) && op1->IsNeverNegative(this) &&
+ op2->IsNeverNegative(this))
{
assert(tree->OperIs(GT_DIV));
- tree->ChangeOper(GT_UDIV);
+ tree->ChangeOper(GT_UDIV, GenTree::PRESERVE_VN);
return fgMorphSmpOp(tree, mac);
}
@@ -11229,13 +11227,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
goto USE_HELPER_FOR_ARITH;
}
- // array.Length is always positive so GT_DIV can be changed to GT_UDIV
- // if op2 is a positive cns
- if (!optValnumCSE_phase && op1->OperIs(GT_ARR_LENGTH) && op2->IsIntegralConst() &&
- op2->AsIntCon()->IconValue() >= 2) // for 0 and 1 it doesn't matter if it's UMOD or MOD
+ // Convert MOD to UMOD if boths op1 and op2 are known to be never negative
+ if (!gtIsActiveCSE_Candidate(tree) && varTypeIsIntegral(tree) && op1->IsNeverNegative(this) &&
+ op2->IsNeverNegative(this))
{
assert(tree->OperIs(GT_MOD));
- tree->ChangeOper(GT_UMOD);
+ tree->ChangeOper(GT_UMOD, GenTree::PRESERVE_VN);
return fgMorphSmpOp(tree, mac);
}
@@ -12646,13 +12643,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_ARITH_EXCPN);
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif
+ case GT_BOUNDS_CHECK:
fgSetRngChkTarget(tree);
break;
diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp
index a8482726a5da23..3511c878827efd 100644
--- a/src/coreclr/jit/morphblock.cpp
+++ b/src/coreclr/jit/morphblock.cpp
@@ -1259,9 +1259,9 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField()
}
else
{
- if (i == 0)
+ if (i == (fieldCnt - 1))
{
- // Use the orginal m_dstAddr tree when i == 0
+ // Reuse the orginal m_dstAddr tree for the last field.
dstAddrClone = m_dstAddr;
}
else
@@ -1381,9 +1381,9 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField()
}
else
{
- if (i == 0)
+ if (i == (fieldCnt - 1))
{
- // Use the orginal m_srcAddr tree when i == 0
+ // Reuse the orginal m_srcAddr tree for the last field.
srcAddrClone = m_srcAddr;
}
else
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index b6dadaac185771..b39b2bd1425c45 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -456,7 +456,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
// If the value number for op2 and tree are different, then some new
// exceptions were produced by op1. For that case we will NOT use the
// normal value. This allows us to CSE commas with an op1 that is
- // an ARR_BOUNDS_CHECK.
+ // an BOUNDS_CHECK.
//
if (vnOp2Lib != vnLib)
{
diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp
index 5fc26491fc6166..ec309793e9376b 100644
--- a/src/coreclr/jit/optimizer.cpp
+++ b/src/coreclr/jit/optimizer.cpp
@@ -8118,8 +8118,8 @@ GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma,
noway_assert(stmt != nullptr);
noway_assert((comma != nullptr && comma->OperIs(GT_COMMA) && comma->gtGetOp1() == check) ||
- (check != nullptr && check->OperIsBoundsCheck() && comma == nullptr));
- noway_assert(check->OperIsBoundsCheck());
+ (check != nullptr && check->OperIs(GT_BOUNDS_CHECK) && comma == nullptr));
+ noway_assert(check->OperIs(GT_BOUNDS_CHECK));
GenTree* tree = comma != nullptr ? comma : check;
@@ -8216,7 +8216,7 @@ void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt)
{
assert(comma != nullptr && comma->OperIs(GT_COMMA));
assert(stmt != nullptr);
- assert(comma->gtGetOp1()->OperIsBoundsCheck());
+ assert(comma->gtGetOp1()->OperIs(GT_BOUNDS_CHECK));
optRemoveRangeCheck(comma->gtGetOp1()->AsBoundsChk(), comma, stmt);
}
diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp
index 0e04acda1a5cd0..4a475806148b7e 100644
--- a/src/coreclr/jit/rangecheck.cpp
+++ b/src/coreclr/jit/rangecheck.cpp
@@ -196,7 +196,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
// If we are not looking at array bounds check, bail.
GenTree* tree = isComma ? treeParent->AsOp()->gtOp1 : treeParent;
- if (!tree->OperIsBoundsCheck())
+ if (!tree->OperIs(GT_BOUNDS_CHECK))
{
return;
}
@@ -222,13 +222,6 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree*
}
}
else
-#ifdef FEATURE_SIMD
- if (tree->gtOper != GT_SIMD_CHK
-#ifdef FEATURE_HW_INTRINSICS
- && tree->gtOper != GT_HW_INTRINSIC_CHK
-#endif // FEATURE_HW_INTRINSICS
- )
-#endif // FEATURE_SIMD
{
arrSize = GetArrLength(arrLenVn);
diff --git a/src/coreclr/jit/simd.cpp b/src/coreclr/jit/simd.cpp
index dd81504a7c9480..4e4bda2ee4a2a8 100644
--- a/src/coreclr/jit/simd.cpp
+++ b/src/coreclr/jit/simd.cpp
@@ -1774,11 +1774,11 @@ GenTree* Compiler::createAddressNodeForSIMDInit(GenTree* tree, unsigned simdSize
// The length for boundary check should be the maximum index number which should be
// (first argument's index number) + (how many array arguments we have) - 1
// = indexVal + arrayElementsCount - 1
- unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
- checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
- GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRef, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
- GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, SCK_ARG_RNG_EXCPN);
+ unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
+ checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRef, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
+ GenTreeBoundsChk* arrBndsChk =
+ new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(checkIndexExpr, arrLen, SCK_ARG_RNG_EXCPN);
offset += OFFSETOF__CORINFO_Array__data;
byrefNode = gtNewOperNode(GT_COMMA, arrayRef->TypeGet(), arrBndsChk, gtCloneExpr(arrayRef));
@@ -2189,8 +2189,7 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
GenTreeArrLen* arrLen =
gtNewArrLen(TYP_INT, arrayRefForArgRngChk, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
- argRngChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, arrLen, SCK_ARG_RNG_EXCPN);
+ argRngChk = new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(index, arrLen, SCK_ARG_RNG_EXCPN);
// Now, clone op3 to create another node for the argChk
GenTree* index2 = gtCloneExpr(op3);
assert(index != nullptr);
@@ -2210,8 +2209,8 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
}
GenTreeArrLen* arrLen =
gtNewArrLen(TYP_INT, arrayRefForArgChk, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
- GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, op2CheckKind);
+ GenTreeBoundsChk* argChk =
+ new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(checkIndexExpr, arrLen, op2CheckKind);
// Create a GT_COMMA tree for the bounds check(s).
op2 = gtNewOperNode(GT_COMMA, op2->TypeGet(), argChk, op2);
diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp
index da20ff6fe22574..54d5d48d4cf2ca 100644
--- a/src/coreclr/jit/stacklevelsetter.cpp
+++ b/src/coreclr/jit/stacklevelsetter.cpp
@@ -131,13 +131,7 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block)
// Check that it uses throw block, find its kind, find the block, set level.
switch (node->OperGet())
{
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
{
GenTreeBoundsChk* bndsChk = node->AsBoundsChk();
SetThrowHelperBlock(bndsChk->gtThrowKind, block);
diff --git a/src/coreclr/jit/unwindarm.cpp b/src/coreclr/jit/unwindarm.cpp
index 54c6a011cb0a70..1eb7456250cbb5 100644
--- a/src/coreclr/jit/unwindarm.cpp
+++ b/src/coreclr/jit/unwindarm.cpp
@@ -1693,7 +1693,12 @@ void UnwindFragmentInfo::Dump(int indent)
printf("%*sUnwindFragmentInfo #%d, @0x%08p, size:%d:\n", indent, "", ufiNum, dspPtr(this), sizeof(*this));
printf("%*s uwiComp: 0x%08p\n", indent, "", dspPtr(uwiComp));
printf("%*s ufiNext: 0x%08p\n", indent, "", dspPtr(ufiNext));
- printf("%*s ufiEmitLoc: 0x%08p\n", indent, "", dspPtr(ufiEmitLoc));
+ printf("%*s ufiEmitLoc: 0x%08p ", indent, "", dspPtr(ufiEmitLoc));
+ if (ufiEmitLoc != nullptr)
+ {
+ ufiEmitLoc->Print(uwiComp->compMethodID);
+ }
+ printf("\n");
printf("%*s ufiHasPhantomProlog: %s\n", indent, "", dspBool(ufiHasPhantomProlog));
printf("%*s %d epilog%s\n", indent, "", count, (count != 1) ? "s" : "");
printf("%*s ufiEpilogList: 0x%08p\n", indent, "", dspPtr(ufiEpilogList));
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index af8d3752199176..99bf197d8efe34 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -6159,13 +6159,7 @@ static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memo
// These need special semantics:
GT_COMMA, // == second argument (but with exception(s) from first).
- GT_ADDR, GT_ARR_BOUNDS_CHECK,
-#ifdef FEATURE_SIMD
- GT_SIMD_CHK,
-#endif
-#ifdef FEATURE_HW_INTRINSICS
- GT_HW_INTRINSIC_CHK,
-#endif
+ GT_ADDR, GT_BOUNDS_CHECK,
GT_OBJ, // May reference heap memory.
GT_BLK, // May reference heap memory.
GT_INIT_VAL, // Not strictly a pass-through.
@@ -9224,13 +9218,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
}
break;
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
+ case GT_BOUNDS_CHECK:
{
ValueNumPair vnpIndex = tree->AsBoundsChk()->GetIndex()->gtVNPair;
ValueNumPair vnpArrLen = tree->AsBoundsChk()->GetArrayLength()->gtVNPair;
@@ -9464,9 +9452,7 @@ void Compiler::fgValueNumberSimd(GenTreeSIMD* tree)
excSetPair = ValueNumStore::VNPForEmptyExcSet();
normalPair = vnStore->VNPairForFunc(tree->TypeGet(), simdFunc);
}
- // TODO-List-Cleanup: the "tree->GetSIMDIntrinsicId() == SIMDIntrinsicInitN" case is a quirk
- // to get zero diffs - Vector2(float, float) was imported with lists - remove it.
- else if ((tree->GetOperandCount() > 2) || (tree->GetSIMDIntrinsicId() == SIMDIntrinsicInitN))
+ else if (tree->GetOperandCount() > 2)
{
// We have a SIMD node with 3 or more args. To retain the
// previous behavior, we will generate a unique VN for this case.
@@ -9816,6 +9802,12 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
}
break;
+ case VNF_JitNewMdArr:
+ {
+ generateUniqueVN = true;
+ }
+ break;
+
case VNF_Box:
case VNF_BoxNullable:
{
@@ -10221,6 +10213,10 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
vnf = VNF_JitNewArr;
break;
+ case CORINFO_HELP_NEW_MDARR:
+ vnf = VNF_JitNewMdArr;
+ break;
+
case CORINFO_HELP_READYTORUN_NEWARR_1:
vnf = VNF_JitReadyToRunNewArr;
break;
@@ -10447,20 +10443,7 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
}
else
{
- // TODO-CQ: this is a list of helpers we're going to treat as non-pure,
- // because they raise complications. Eventually, we need to handle those complications...
- bool needsFurtherWork = false;
- switch (helpFunc)
- {
- case CORINFO_HELP_NEW_MDARR:
- // This is a varargs helper. We need to represent the array shape in the VN world somehow.
- needsFurtherWork = true;
- break;
- default:
- break;
- }
-
- if (!needsFurtherWork && (pure || isAlloc))
+ if (pure || isAlloc)
{
VNFunc vnf = fgValueNumberJitHelperMethodVNFunc(helpFunc);
@@ -10974,13 +10957,7 @@ void Compiler::fgValueNumberAddExceptionSet(GenTree* tree)
fgValueNumberAddExceptionSetForDivision(tree);
break;
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HW_INTRINSIC_CHK:
-#endif // FEATURE_HW_INTRINSICS
- case GT_ARR_BOUNDS_CHECK:
+ case GT_BOUNDS_CHECK:
fgValueNumberAddExceptionSetForBoundsCheck(tree);
break;
diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h
index a45044b6620357..289e09fb688b13 100644
--- a/src/coreclr/jit/valuenum.h
+++ b/src/coreclr/jit/valuenum.h
@@ -712,11 +712,11 @@ class ValueNumStore
typedef SmallHashTable CheckedBoundVNSet;
// Returns true if the VN is known or likely to appear as the conservative value number
- // of the length argument to a GT_ARR_BOUNDS_CHECK node.
+ // of the length argument to a GT_BOUNDS_CHECK node.
bool IsVNCheckedBound(ValueNum vn);
// Record that a VN is known to appear as the conservative value number of the length
- // argument to a GT_ARR_BOUNDS_CHECK node.
+ // argument to a GT_BOUNDS_CHECK node.
void SetVNIsCheckedBound(ValueNum vn);
// Information about the individual components of a value number representing an unsigned
diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h
index d5a81d03ca5022..ac97b94f36a6f2 100644
--- a/src/coreclr/jit/valuenumfuncs.h
+++ b/src/coreclr/jit/valuenumfuncs.h
@@ -132,6 +132,7 @@ ValueNumFuncDef(GetStaticAddrTLS, 1, false, true, false)
ValueNumFuncDef(JitNew, 2, false, true, false)
ValueNumFuncDef(JitNewArr, 3, false, true, false)
+ValueNumFuncDef(JitNewMdArr, 4, false, true, false)
ValueNumFuncDef(JitReadyToRunNew, 2, false, true, false)
ValueNumFuncDef(JitReadyToRunNewArr, 3, false, true, false)
ValueNumFuncDef(Box, 3, false, false, false)
diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
index fa3607b93b9775..a975e3356144cb 100644
--- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
+++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
@@ -10,7 +10,7 @@ struct ReadyToRunHeaderConstants
{
static const uint32_t Signature = 0x00525452; // 'RTR'
- static const uint32_t CurrentMajorVersion = 5;
+ static const uint32_t CurrentMajorVersion = 6;
static const uint32_t CurrentMinorVersion = 0;
};
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx
index 85a0a88f88e5f3..1f979fe12d4eb4 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx
@@ -886,7 +886,7 @@
The name can be no more than {0} characters in length.
- Object is not a array with the same number of elements as the array to compare it to.
+ The object is not an array with the same number of elements as the array to compare it to.Argument must be of type {0}.
diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp
index f9d591bb849478..76135d655f9349 100644
--- a/src/coreclr/pal/src/thread/process.cpp
+++ b/src/coreclr/pal/src/thread/process.cpp
@@ -83,7 +83,6 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
#include
#include
#include
-#if defined(HOST_ARM64)
#include
#include
extern "C"
@@ -101,7 +100,6 @@ extern "C"
} \
} while (false)
-#endif // defined(HOST_ARM64)
#endif // __APPLE__
#ifdef __NetBSD__
@@ -3486,7 +3484,7 @@ InitializeFlushProcessWriteBuffers()
}
}
-#if defined(TARGET_OSX) && defined(HOST_ARM64)
+#ifdef TARGET_OSX
return TRUE;
#else
s_helperPage = static_cast(mmap(0, GetVirtualPageSize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
@@ -3516,7 +3514,7 @@ InitializeFlushProcessWriteBuffers()
}
return status == 0;
-#endif // defined(TARGET_OSX) && defined(HOST_ARM64)
+#endif // TARGET_OSX
}
#define FATAL_ASSERT(e, msg) \
@@ -3566,7 +3564,7 @@ FlushProcessWriteBuffers()
status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
}
-#if defined(TARGET_OSX) && defined(HOST_ARM64)
+#ifdef TARGET_OSX
else
{
mach_msg_type_number_t cThreads;
@@ -3592,7 +3590,7 @@ FlushProcessWriteBuffers()
machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t));
CHECK_MACH("vm_deallocate()", machret);
}
-#endif // defined(TARGET_OSX) && defined(HOST_ARM64)
+#endif // TARGET_OSX
}
/*++
diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
index 6f718756b205fb..86cbc56da8a807 100644
--- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
+++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
@@ -214,7 +214,6 @@ public enum ReadyToRunHelper
Unbox = 0x5A,
Unbox_Nullable = 0x5B,
NewMultiDimArr = 0x5C,
- NewMultiDimArr_NonVarArg = 0x5D,
// Helpers used with generic handle lookup cases
NewObject = 0x60,
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 0765a56b21e199..6d8a5ec684f523 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -56,8 +56,7 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_NEWSFAST_ALIGN8, // allocator for small, non-finalizer, non-array object, 8 byte aligned
CORINFO_HELP_NEWSFAST_ALIGN8_VC,// allocator for small, value class, 8 byte aligned
CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE, // allocator for small, finalizable, non-array object, 8 byte aligned
- CORINFO_HELP_NEW_MDARR, // multi-dim array helper (with or without lower bounds - dimensions passed in as vararg)
- CORINFO_HELP_NEW_MDARR_NONVARARG,// multi-dim array helper (with or without lower bounds - dimensions passed in as unmanaged array)
+ CORINFO_HELP_NEW_MDARR,// multi-dim array helper (with or without lower bounds - dimensions passed in as unmanaged array)
CORINFO_HELP_NEWARR_1_DIRECT, // helper for any one dimensional array creation
CORINFO_HELP_NEWARR_1_OBJ, // optimized 1-D object arrays
CORINFO_HELP_NEWARR_1_VC, // optimized 1-D value class arrays
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs
new file mode 100644
index 00000000000000..4e8ffb80febb79
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection.Metadata;
+
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ internal static class ReflectionInvokeSupportDependencyAlgorithm
+ {
+ // Inserts dependencies to make the following corner case work (we need MethodTable for `MyStruct[]`):
+ //
+ // struct MyStruct
+ // {
+ // public static int Count(params MyStruct[] myStructs)
+ // {
+ // return myStructs.Length;
+ // }
+ //
+ // public static void Main()
+ // {
+ // typeof(MyStruct).InvokeMember(nameof(Count), BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { default(MyStruct) });
+ // }
+ // }
+ public static void GetDependenciesFromParamsArray(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
+ {
+ MethodSignature sig = method.Signature;
+ if (sig.Length < 1 || !sig[sig.Length - 1].IsArray)
+ return;
+
+ if (method.GetTypicalMethodDefinition() is not EcmaMethod ecmaMethod)
+ return;
+
+ MetadataReader reader = ecmaMethod.MetadataReader;
+ MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle);
+
+ foreach (ParameterHandle paramHandle in methodDef.GetParameters())
+ {
+ Parameter param = reader.GetParameter(paramHandle);
+ if (param.SequenceNumber == sig.Length /* SequenceNumber is 1-based */)
+ {
+ if (!reader.GetCustomAttributeHandle(param.GetCustomAttributes(), "System", "ParamArrayAttribute").IsNil)
+ {
+ dependencies ??= new DependencyList();
+ dependencies.Add(
+ factory.ConstructedTypeSymbol(sig[sig.Length - 1].NormalizeInstantiation()),
+ "Reflection invoke");
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs
index 8259de25d6c8fd..1a775400feafd6 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs
@@ -94,7 +94,7 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
mangledName = "RhUnboxNullable";
break;
- case ReadyToRunHelper.NewMultiDimArr_NonVarArg:
+ case ReadyToRunHelper.NewMultiDimArr:
methodDesc = context.GetHelperEntryPoint("ArrayHelpers", "NewObjArray");
break;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
index 837171d9059c1c..d908623c91c4ef 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
@@ -332,6 +332,8 @@ public void GetDependenciesDueToReflectability(ref DependencyList dependencies,
{
// We're going to generate a mapping table entry for this. Collect dependencies.
ReflectionInvokeMapNode.AddDependenciesDueToReflectability(ref dependencies, factory, method);
+
+ ReflectionInvokeSupportDependencyAlgorithm.GetDependenciesFromParamsArray(ref dependencies, factory, method);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
index 45591657c1c3b3..687104277c2f2b 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
@@ -245,7 +245,15 @@ protected override void GetMetadataDependenciesDueToReflectability(ref Dependenc
bool fullyRoot;
string reason;
- if (_rootEntireAssembliesModules.Contains(assemblyName))
+ // Compat with https://github.com/dotnet/linker/issues/1541 IL Linker bug:
+ // Asking to root an assembly with entrypoint will not actually root things in the assembly.
+ // We need to emulate this because the SDK injects a root for the entrypoint assembly right now
+ // because of IL Linker's implementation details (IL Linker won't root Main() by itself).
+ // TODO: We should technically reflection-root Main() here but hopefully the above issue
+ // will be fixed before it comes to that being necessary.
+ bool isEntrypointAssembly = module is EcmaModule ecmaModule && ecmaModule.PEReader.PEHeaders.IsExe;
+
+ if (!isEntrypointAssembly && _rootEntireAssembliesModules.Contains(assemblyName))
{
// If the assembly was specified as a root on the command line, root it
fullyRoot = true;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs
index 32131808adece0..e868b3e1f418c3 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs
@@ -310,7 +310,7 @@ private void ImportCall(ILOpcode opcode, int token)
{
// RyuJIT is going to call the "MdArray" creation helper even if this is an SzArray,
// hence the IsArray check above. Note that the MdArray helper can handle SzArrays.
- _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason);
+ _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr), reason);
return;
}
else
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index af1e7859356933..c37b562a1383a2 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -204,6 +204,7 @@
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs
index 6fcb4e2c1d287c..ad294d7227d464 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs
@@ -69,13 +69,21 @@ public void GetCallRefMap(MethodDesc method, bool isUnboxingStub)
{
TransitionBlock transitionBlock = TransitionBlock.FromTarget(method.Context.Target);
- bool hasThis = (method.Signature.Flags & MethodSignatureFlags.Static) == 0;
+ MethodSignature signature = method.Signature;
+
+ bool hasThis = (signature.Flags & MethodSignatureFlags.Static) == 0;
+
+ // This pointer is omitted for string constructors
+ bool fCtorOfVariableSizedObject = hasThis && method.OwningType.IsString && method.IsConstructor;
+ if (fCtorOfVariableSizedObject)
+ hasThis = false;
+
bool isVarArg = false;
- TypeHandle returnType = new TypeHandle(method.Signature.ReturnType);
- TypeHandle[] parameterTypes = new TypeHandle[method.Signature.Length];
+ TypeHandle returnType = new TypeHandle(signature.ReturnType);
+ TypeHandle[] parameterTypes = new TypeHandle[signature.Length];
for (int parameterIndex = 0; parameterIndex < parameterTypes.Length; parameterIndex++)
{
- parameterTypes[parameterIndex] = new TypeHandle(method.Signature[parameterIndex]);
+ parameterTypes[parameterIndex] = new TypeHandle(signature[parameterIndex]);
}
CallingConventions callingConventions = (hasThis ? CallingConventions.ManagedInstance : CallingConventions.ManagedStatic);
bool hasParamType = method.RequiresInstArg() && !isUnboxingStub;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index 597a8fa36559b2..f8282ab82cd27c 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -704,9 +704,6 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
case CorInfoHelpFunc.CORINFO_HELP_NEW_MDARR:
id = ReadyToRunHelper.NewMultiDimArr;
break;
- case CorInfoHelpFunc.CORINFO_HELP_NEW_MDARR_NONVARARG:
- id = ReadyToRunHelper.NewMultiDimArr_NonVarArg;
- break;
case CorInfoHelpFunc.CORINFO_HELP_NEWFAST:
id = ReadyToRunHelper.NewObject;
break;
diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs
index a71ad97519f5c3..8ec3cf31f332e0 100644
--- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs
@@ -1726,10 +1726,6 @@ private void ParseHelper(StringBuilder builder)
builder.Append("NEW_MULTI_DIM_ARR");
break;
- case ReadyToRunHelper.NewMultiDimArr_NonVarArg:
- builder.Append("NEW_MULTI_DIM_ARR__NON_VAR_ARG");
- break;
-
case ReadyToRunHelper.MonitorEnter:
builder.Append("MONITOR_ENTER");
break;
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
index 2ecd15ab009959..885873032fd720 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
@@ -501,8 +501,8 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
case CorInfoHelpFunc.CORINFO_HELP_UNBOX_NULLABLE:
id = ReadyToRunHelper.Unbox_Nullable;
break;
- case CorInfoHelpFunc.CORINFO_HELP_NEW_MDARR_NONVARARG:
- id = ReadyToRunHelper.NewMultiDimArr_NonVarArg;
+ case CorInfoHelpFunc.CORINFO_HELP_NEW_MDARR:
+ id = ReadyToRunHelper.NewMultiDimArr;
break;
case CorInfoHelpFunc.CORINFO_HELP_NEWFAST:
id = ReadyToRunHelper.NewObject;
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 6fb9d0e3e3f210..919a5352c1bb0a 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -864,15 +864,15 @@ DEFINE_FIELD(BITCONVERTER, ISLITTLEENDIAN, IsLittleEndian)
DEFINE_FIELD(STRING, M_FIRST_CHAR, _firstChar)
DEFINE_FIELD(STRING, EMPTY, Empty)
DEFINE_METHOD(STRING, CTOR_CHARPTR, .ctor, IM_PtrChar_RetVoid)
-DEFINE_METHOD(STRING, CTORF_CHARARRAY, Ctor, IM_ArrChar_RetStr)
-DEFINE_METHOD(STRING, CTORF_CHARARRAY_START_LEN,Ctor, IM_ArrChar_Int_Int_RetStr)
-DEFINE_METHOD(STRING, CTORF_CHAR_COUNT, Ctor, IM_Char_Int_RetStr)
-DEFINE_METHOD(STRING, CTORF_CHARPTR, Ctor, IM_PtrChar_RetStr)
-DEFINE_METHOD(STRING, CTORF_CHARPTR_START_LEN,Ctor, IM_PtrChar_Int_Int_RetStr)
-DEFINE_METHOD(STRING, CTORF_READONLYSPANOFCHAR,Ctor, IM_ReadOnlySpanOfChar_RetStr)
-DEFINE_METHOD(STRING, CTORF_SBYTEPTR, Ctor, IM_PtrSByt_RetStr)
-DEFINE_METHOD(STRING, CTORF_SBYTEPTR_START_LEN, Ctor, IM_PtrSByt_Int_Int_RetStr)
-DEFINE_METHOD(STRING, CTORF_SBYTEPTR_START_LEN_ENCODING, Ctor, IM_PtrSByt_Int_Int_Encoding_RetStr)
+DEFINE_METHOD(STRING, CTORF_CHARARRAY, Ctor, SM_ArrChar_RetStr)
+DEFINE_METHOD(STRING, CTORF_CHARARRAY_START_LEN,Ctor, SM_ArrChar_Int_Int_RetStr)
+DEFINE_METHOD(STRING, CTORF_CHAR_COUNT, Ctor, SM_Char_Int_RetStr)
+DEFINE_METHOD(STRING, CTORF_CHARPTR, Ctor, SM_PtrChar_RetStr)
+DEFINE_METHOD(STRING, CTORF_CHARPTR_START_LEN,Ctor, SM_PtrChar_Int_Int_RetStr)
+DEFINE_METHOD(STRING, CTORF_READONLYSPANOFCHAR,Ctor, SM_ReadOnlySpanOfChar_RetStr)
+DEFINE_METHOD(STRING, CTORF_SBYTEPTR, Ctor, SM_PtrSByt_RetStr)
+DEFINE_METHOD(STRING, CTORF_SBYTEPTR_START_LEN, Ctor, SM_PtrSByt_Int_Int_RetStr)
+DEFINE_METHOD(STRING, CTORF_SBYTEPTR_START_LEN_ENCODING, Ctor, SM_PtrSByt_Int_Int_Encoding_RetStr)
DEFINE_METHOD(STRING, INTERNAL_COPY, InternalCopy, SM_Str_IntPtr_Int_RetVoid)
DEFINE_METHOD(STRING, WCSLEN, wcslen, SM_PtrChar_RetInt)
DEFINE_METHOD(STRING, STRLEN, strlen, SM_PtrByte_RetInt)
diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h
index 997967d2e13c88..e0816058bb3b8e 100644
--- a/src/coreclr/vm/fcall.h
+++ b/src/coreclr/vm/fcall.h
@@ -1143,7 +1143,6 @@ struct FCSigCheck {
#define HCIMPL2_RAW(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1) {
#define HCIMPL2_VV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, int /* ECX */, a2, a1) { HCIMPL_PROLOG(funcname)
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(int /* EAX */, int /* EDX */, a1, a2) { HCIMPL_PROLOG(funcname)
-#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) { HCIMPL_PROLOG(funcname)
#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(int /* EAX */, a2, a1, a4, a3) { HCIMPL_PROLOG(funcname)
@@ -1168,7 +1167,6 @@ struct FCSigCheck {
#define HCIMPL2_RAW(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) {
#define HCIMPL2_VV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a2, a1) { HCIMPL_PROLOG(funcname)
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname)
-#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname)
#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a4, a3) { HCIMPL_PROLOG(funcname)
@@ -1194,7 +1192,6 @@ struct FCSigCheck {
#define HCIMPL2_RAW(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) {
#define HCIMPL2_VV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname)
#define HCIMPL2_IV(rettype, funcname, a1, a2) rettype F_CALL_CONV funcname(a1, a2) { HCIMPL_PROLOG(funcname)
-#define HCIMPL2VA(rettype, funcname, a1, a2) rettype F_CALL_VA_CONV funcname(a1, a2, ...) { HCIMPL_PROLOG(funcname)
#define HCIMPL3(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) { HCIMPL_PROLOG(funcname)
#define HCIMPL3_RAW(rettype, funcname, a1, a2, a3) rettype F_CALL_CONV funcname(a1, a2, a3) {
#define HCIMPL4(rettype, funcname, a1, a2, a3, a4) rettype F_CALL_CONV funcname(a1, a2, a3, a4) { HCIMPL_PROLOG(funcname)
diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp
index d5de56de06a67f..77bcb178bb49c2 100644
--- a/src/coreclr/vm/frames.cpp
+++ b/src/coreclr/vm/frames.cpp
@@ -1269,6 +1269,10 @@ void TransitionFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
MetaSig msig(pSig, cbSigSize, pFunction->GetModule(), &typeContext);
+ bool fCtorOfVariableSizedObject = msig.HasThis() && (pFunction->GetMethodTable() == g_pStringClass) && pFunction->IsCtor();
+ if (fCtorOfVariableSizedObject)
+ msig.ClearHasThis();
+
if (pFunction->RequiresInstArg() && !SuppressParamTypeArg())
msig.SetHasParamTypeArg();
@@ -2101,6 +2105,12 @@ void ComputeCallRefMap(MethodDesc* pMD,
pMD->GetSig(&pSig, &cbSigSize);
MetaSig msig(pSig, cbSigSize, pMD->GetModule(), &typeContext);
+ bool fCtorOfVariableSizedObject = msig.HasThis() && (pMD->GetMethodTable() == g_pStringClass) && pMD->IsCtor();
+ if (fCtorOfVariableSizedObject)
+ {
+ msig.ClearHasThis();
+ }
+
//
// Shared default interface methods (i.e. virtual interface methods with an implementation) require
// an instantiation argument. But if we're in a situation where we haven't resolved the method yet
diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp
index ede736543d52b5..4782df6281b128 100644
--- a/src/coreclr/vm/ilmarshalers.cpp
+++ b/src/coreclr/vm/ilmarshalers.cpp
@@ -1635,14 +1635,13 @@ void ILVBByValStrWMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILE
EmitLoadNativeValue(pslILEmit);
pslILEmit->EmitBRFALSE(pNullRefLabel);
- pslILEmit->EmitLDNULL(); // this
EmitLoadNativeValue(pslILEmit); // ptr
pslILEmit->EmitLDC(0); // startIndex
pslILEmit->EmitLDLOC(m_dwCCHLocal); // length
// String CtorCharPtrStartLength(char *ptr, int startIndex, int length)
// TODO Phase5: Why do we call this weirdo?
- pslILEmit->EmitCALL(METHOD__STRING__CTORF_CHARPTR_START_LEN, 4, 1);
+ pslILEmit->EmitCALL(METHOD__STRING__CTORF_CHARPTR_START_LEN, 3, 1);
EmitStoreManagedValue(pslILEmit);
pslILEmit->EmitLabel(pNullRefLabel);
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 7672d6afe766b7..3e92144dca122b 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2639,74 +2639,8 @@ HCIMPL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
}
HCIMPLEND
-/*********************************************************************
-// Allocate a multi-dimensional array
-*/
-OBJECTREF allocNewMDArr(TypeHandle typeHnd, unsigned dwNumArgs, va_list args)
-{
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- PRECONDITION(dwNumArgs > 0);
- } CONTRACTL_END;
-
- // Get the arguments in the right order
-
- INT32* fwdArgList;
-
-#ifdef TARGET_X86
- fwdArgList = (INT32*)args;
-
- // reverse the order
- INT32* p = fwdArgList;
- INT32* q = fwdArgList + (dwNumArgs-1);
- while (p < q)
- {
- INT32 t = *p; *p = *q; *q = t;
- p++; q--;
- }
-#else
- // create an array where fwdArgList[0] == arg[0] ...
- fwdArgList = (INT32*) _alloca(dwNumArgs * sizeof(INT32));
- for (unsigned i = 0; i < dwNumArgs; i++)
- {
- fwdArgList[i] = va_arg(args, INT32);
- }
-#endif
-
- return AllocateArrayEx(typeHnd, fwdArgList, dwNumArgs);
-}
-
-/*********************************************************************
-// Allocate a multi-dimensional array with lower bounds specified.
-// The caller pushes both sizes AND/OR bounds for every dimension
-*/
-
-HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs)
-{
- FCALL_CONTRACT;
-
- OBJECTREF ret = 0;
- HELPER_METHOD_FRAME_BEGIN_RET_1(ret); // Set up a frame
-
- TypeHandle typeHnd(classHnd);
- typeHnd.CheckRestore();
- _ASSERTE(typeHnd.GetMethodTable()->IsArray());
-
- va_list dimsAndBounds;
- va_start(dimsAndBounds, dwNumArgs);
-
- ret = allocNewMDArr(typeHnd, dwNumArgs, dimsAndBounds);
- va_end(dimsAndBounds);
-
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(ret);
-}
-HCIMPLEND
-
/*************************************************************/
-HCIMPL3(Object*, JIT_NewMDArrNonVarArg, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs, INT32 * pArgList)
+HCIMPL3(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs, INT32 * pArgList)
{
FCALL_CONTRACT;
@@ -5182,7 +5116,10 @@ void JIT_Patchpoint(int* counter, int ilOffset)
#endif
SetSP(&frameContext, currentSP);
+
+#if defined(TARGET_AMD64)
frameContext.Rbp = currentFP;
+#endif
// Note we can get here w/o triggering, if there is an existing OSR method and
// we hit the patchpoint.
@@ -5345,7 +5282,10 @@ void JIT_PartialCompilationPatchpoint(int ilOffset)
#endif
SetSP(&frameContext, currentSP);
+
+#if defined(TARGET_AMD64)
frameContext.Rbp = currentFP;
+#endif
// Note we can get here w/o triggering, if there is an existing OSR method and
// we hit the patchpoint.
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index a684163030d566..54d3d4780b6f22 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -366,7 +366,6 @@ DEFINE_METASIG_T(SM(Type_RetInt, C(TYPE), i))
DEFINE_METASIG(SM(ArrByte_RetObj, a(b), j))
DEFINE_METASIG(SM(ArrByte_Bool_RetObj, a(b) F, j))
DEFINE_METASIG(SM(ArrByte_ArrByte_RefObj_RetObj, a(b) a(b) r(j), j))
-DEFINE_METASIG_T(SM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
DEFINE_METASIG_T(SM(UInt_UInt_PtrNativeOverlapped_RetVoid, K K P(g(NATIVEOVERLAPPED)), v))
@@ -410,15 +409,15 @@ DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetVoid, P(B) i i C(ENCODING), v))
DEFINE_METASIG(IM(PtrChar_Int_RetVoid, P(u) i, v))
DEFINE_METASIG(IM(PtrSByt_Int_RetVoid, P(B) i, v))
-DEFINE_METASIG(IM(ArrChar_RetStr, a(u), s))
-DEFINE_METASIG(IM(ArrChar_Int_Int_RetStr, a(u) i i, s))
-DEFINE_METASIG(IM(Char_Int_RetStr, u i, s))
-DEFINE_METASIG(IM(PtrChar_RetStr, P(u), s))
-DEFINE_METASIG(IM(PtrChar_Int_Int_RetStr, P(u) i i, s))
-DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetStr, GI(g(READONLY_SPAN), 1, u), s))
-DEFINE_METASIG(IM(PtrSByt_RetStr, P(B), s))
-DEFINE_METASIG(IM(PtrSByt_Int_Int_RetStr, P(B) i i, s))
-DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
+DEFINE_METASIG(SM(ArrChar_RetStr, a(u), s))
+DEFINE_METASIG(SM(ArrChar_Int_Int_RetStr, a(u) i i, s))
+DEFINE_METASIG(SM(Char_Int_RetStr, u i, s))
+DEFINE_METASIG(SM(PtrChar_RetStr, P(u), s))
+DEFINE_METASIG(SM(PtrChar_Int_Int_RetStr, P(u) i i, s))
+DEFINE_METASIG_T(SM(ReadOnlySpanOfChar_RetStr, GI(g(READONLY_SPAN), 1, u), s))
+DEFINE_METASIG(SM(PtrSByt_RetStr, P(B), s))
+DEFINE_METASIG(SM(PtrSByt_Int_Int_RetStr, P(B) i i, s))
+DEFINE_METASIG_T(SM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))
DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v))
diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp
index 68268942e562f5..8b61726f447267 100644
--- a/src/coreclr/vm/reflectioninvocation.cpp
+++ b/src/coreclr/vm/reflectioninvocation.cpp
@@ -540,6 +540,7 @@ class ArgIteratorBaseForMethodInvoke
{
protected:
SIGNATURENATIVEREF * m_ppNativeSig;
+ bool m_fHasThis;
FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
{
@@ -567,7 +568,7 @@ class ArgIteratorBaseForMethodInvoke
BOOL HasThis()
{
LIMITED_METHOD_CONTRACT;
- return (*m_ppNativeSig)->HasThis();
+ return m_fHasThis;
}
BOOL HasParamType()
@@ -602,10 +603,12 @@ class ArgIteratorBaseForMethodInvoke
class ArgIteratorForMethodInvoke : public ArgIteratorTemplate
{
public:
- ArgIteratorForMethodInvoke(SIGNATURENATIVEREF * ppNativeSig)
+ ArgIteratorForMethodInvoke(SIGNATURENATIVEREF * ppNativeSig, BOOL fCtorOfVariableSizedObject)
{
m_ppNativeSig = ppNativeSig;
+ m_fHasThis = (*m_ppNativeSig)->HasThis() && !fCtorOfVariableSizedObject;
+
DWORD dwFlags = (*m_ppNativeSig)->GetArgIteratorFlags();
// Use the cached values if they are available
@@ -803,7 +806,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
}
{
- ArgIteratorForMethodInvoke argit(&gc.pSig);
+ ArgIteratorForMethodInvoke argit(&gc.pSig, fCtorOfVariableSizedObject);
if (argit.IsActivationNeeded())
pMeth->EnsureActive();
@@ -888,7 +891,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
}
// Copy "this" pointer
- if (!pMeth->IsStatic()) {
+ if (!pMeth->IsStatic() && !fCtorOfVariableSizedObject) {
PVOID pThisPtr;
if (fConstructor)
diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp
index 7489cd349f0562..25ea2d4d0a1a50 100644
--- a/src/coreclr/vm/siginfo.hpp
+++ b/src/coreclr/vm/siginfo.hpp
@@ -1109,6 +1109,11 @@ class MetaSig
m_flags |= TREAT_AS_VARARG;
}
+ void ClearHasThis()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_CallConv &= ~IMAGE_CEE_CS_CALLCONV_HASTHIS;
+ }
// These are protected because Reflection subclasses Metasig
protected:
diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
index a9cfadd7e35ae8..48379e2b505509 100644
--- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
+++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
@@ -1244,6 +1244,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/58812", TestPlatforms.Browser)]
public async Task Dispose_DisposingHandlerCancelsActiveOperationsWithoutResponses()
{
if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value)
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
index 5f3a0f5b82a6da..6564a97b17fd27 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
@@ -565,6 +565,7 @@ public static void DecryptPkcs12WithBytes()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void DecryptPkcs12PbeTooManyIterations()
{
// pbeWithSHAAnd3-KeyTripleDES-CBC with 600,001 iterations
@@ -585,6 +586,7 @@ public static void DecryptPkcs12PbeTooManyIterations()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void ReadWriteDsa1024EncryptedPkcs8_Pbes2HighIterations()
{
// pkcs5PBES2 hmacWithSHA256 aes128-CBC with 600,001 iterations
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs
index dcfe4e81af9694..689e1391cb3eed 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs
@@ -959,6 +959,7 @@ public void DecryptPkcs12WithBytes()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public void DecryptPkcs12PbeTooManyIterations()
{
// pbeWithSHAAnd3-KeyTripleDES-CBC with 600,001 iterations
@@ -976,6 +977,7 @@ public void DecryptPkcs12PbeTooManyIterations()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public void ReadWriteEc256EncryptedPkcs8_Pbes2HighIterations()
{
// pkcs5PBES2 hmacWithSHA256 aes128-CBC with 600,001 iterations
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs
index e2040d47888d3d..eb12b87acba738 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs
@@ -7,6 +7,7 @@
namespace System.Security.Cryptography.EcDsa.Tests
{
[SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public sealed class ECDsaTests_Span : ECDsaTests
{
protected override bool VerifyData(ECDsa ecdsa, byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm) =>
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs
index f3f9b1b85e8c04..d79be4e69f2f0a 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs
@@ -107,6 +107,7 @@ public static void ReadWriteBigExponentPrivatePkcs1()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void ReadWriteDiminishedDPPrivatePkcs1()
{
ReadWriteBase64PrivatePkcs1(
@@ -789,6 +790,7 @@ public static void ReadPbes2Rc2EncryptedDiminishedDP_PasswordBytes()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void ReadEncryptedDiminishedDP_EmptyPassword()
{
// [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test key.")]
@@ -1254,6 +1256,7 @@ public static void DecryptPkcs12WithBytes()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void DecryptPkcs12PbeTooManyIterations()
{
// pbeWithSHAAnd3-KeyTripleDES-CBC with 600,001 iterations
@@ -1290,6 +1293,7 @@ public static void DecryptPkcs12PbeTooManyIterations()
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/62547", TestPlatforms.Android)]
public static void ReadWriteRsa2048EncryptedPkcs8_Pbes2HighIterations()
{
// pkcs5PBES2 hmacWithSHA256 aes128-CBC with 600,001 iterations
diff --git a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/EnvironmentVariablesConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/EnvironmentVariablesConfigurationProvider.cs
index 8b88483620cc50..73e696f201b2c3 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/EnvironmentVariablesConfigurationProvider.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/EnvironmentVariablesConfigurationProvider.cs
@@ -78,17 +78,9 @@ internal void Load(IDictionary envVariables)
{
prefix = CustomPrefix;
}
- else if (key.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase))
- {
- // This prevents the prefix from being normalized.
- // We can also do a fast path branch, I guess? No point in reallocating if the prefix is empty.
- key = NormalizeKey(key.Substring(_prefix.Length));
- data[key] = entry.Value as string;
-
- continue;
- }
else
{
+ AddIfPrefixed(data, NormalizeKey(key), (string?)entry.Value);
continue;
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/tests/EnvironmentVariablesTest.cs b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/tests/EnvironmentVariablesTest.cs
index c5b1030ee74a0b..1e98a5767096a0 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/tests/EnvironmentVariablesTest.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/tests/EnvironmentVariablesTest.cs
@@ -166,7 +166,7 @@ public void ReplaceDoubleUnderscoreInEnvironmentVariablesButNotPrefix()
envConfigSrc.Load(dict);
- Assert.Equal("connection", envConfigSrc.Get("data:ConnectionString"));
+ Assert.Throws(() => envConfigSrc.Get("data:ConnectionString"));
}
[Fact]
@@ -176,7 +176,7 @@ public void ReplaceDoubleUnderscoreInEnvironmentVariablesButNotInAnomalousPrefix
{
{"_____EXPERIMENTAL__data__ConnectionString", "connection"}
};
- var envConfigSrc = new EnvironmentVariablesConfigurationProvider("_____EXPERIMENTAL__");
+ var envConfigSrc = new EnvironmentVariablesConfigurationProvider("::_EXPERIMENTAL:");
envConfigSrc.Load(dict);
@@ -194,7 +194,7 @@ public void ReplaceDoubleUnderscoreInEnvironmentVariablesWithDuplicatedPrefix()
envConfigSrc.Load(dict);
- Assert.Equal("connection", envConfigSrc.Get("test:ConnectionString"));
+ Assert.Throws(() => envConfigSrc.Get("test:ConnectionString"));
}
[Fact]
@@ -205,7 +205,7 @@ public void PrefixPreventsLoadingSqlConnectionStrings()
{"test__test__ConnectionString", "connection"},
{"SQLCONNSTR_db1", "connStr"}
};
- var envConfigSrc = new EnvironmentVariablesConfigurationProvider("test__");
+ var envConfigSrc = new EnvironmentVariablesConfigurationProvider("test:");
envConfigSrc.Load(dict);
@@ -213,6 +213,54 @@ public void PrefixPreventsLoadingSqlConnectionStrings()
Assert.Throws(() => envConfigSrc.Get("ConnectionStrings:db1_ProviderName"));
}
+ public const string EnvironmentVariable = "Microsoft__Extensions__Configuration__EnvironmentVariables__Test__Foo";
+ public class SettingsWithFoo
+ {
+ public string? Foo { get; set; }
+ }
+
+ [Fact]
+ public void AddEnvironmentVariables_Bind_PrefixShouldNormalize()
+ {
+ try
+ {
+ Environment.SetEnvironmentVariable(EnvironmentVariable, "myFooValue");
+ var configuration = new ConfigurationBuilder()
+ .AddEnvironmentVariables("Microsoft:Extensions:Configuration:EnvironmentVariables:Test:")
+ .Build();
+
+ var settingsWithFoo = new SettingsWithFoo();
+ configuration.Bind(settingsWithFoo);
+
+ Assert.Equal("myFooValue", settingsWithFoo.Foo);
+ }
+ finally
+ {
+ Environment.SetEnvironmentVariable(EnvironmentVariable, null);
+ }
+ }
+
+ [Fact]
+ public void AddEnvironmentVariables_UsingDoubleUnderscores_Bind_PrefixWontNormalize()
+ {
+ try
+ {
+ Environment.SetEnvironmentVariable(EnvironmentVariable, "myFooValue");
+ var configuration = new ConfigurationBuilder()
+ .AddEnvironmentVariables("Microsoft__Extensions__Configuration__EnvironmentVariables__Test__")
+ .Build();
+
+ var settingsWithFoo = new SettingsWithFoo();
+ configuration.Bind(settingsWithFoo);
+
+ Assert.Null(settingsWithFoo.Foo);
+ }
+ finally
+ {
+ Environment.SetEnvironmentVariable(EnvironmentVariable, null);
+ }
+ }
+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public void BindingDoesNotThrowIfReloadedDuringBinding()
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/UserSecretsConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/UserSecretsConfigurationExtensions.cs
index 86139faf9b021d..963ac77a30eea6 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/UserSecretsConfigurationExtensions.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/UserSecretsConfigurationExtensions.cs
@@ -29,7 +29,7 @@ public static class UserSecretsConfigurationExtensions
/// The configuration builder.
public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration)
where T : class
- => configuration.AddUserSecrets(typeof(T).Assembly, optional: false, reloadOnChange: false);
+ => configuration.AddUserSecrets(typeof(T).Assembly, optional: true, reloadOnChange: false);
///
///
@@ -82,7 +82,7 @@ public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder
/// Thrown when does not have a valid
/// The configuration builder.
public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration, Assembly assembly)
- => configuration.AddUserSecrets(assembly, optional: false, reloadOnChange: false);
+ => configuration.AddUserSecrets(assembly, optional: true, reloadOnChange: false);
///
///
diff --git a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs
index e0d86e456227c6..82391a90645af6 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs
@@ -87,23 +87,23 @@ public void AddUserSecrets_FindsAssemblyAttributeFromType()
public void AddUserSecrets_ThrowsIfAssemblyAttributeFromType()
{
var ex = Assert.Throws(() =>
- new ConfigurationBuilder().AddUserSecrets());
+ new ConfigurationBuilder().AddUserSecrets(optional: false));
Assert.Equal(SR.Format(SR.Error_Missing_UserSecretsIdAttribute, typeof(string).Assembly.GetName().Name),
ex.Message);
ex = Assert.Throws(() =>
- new ConfigurationBuilder().AddUserSecrets(typeof(JObject).Assembly));
+ new ConfigurationBuilder().AddUserSecrets(typeof(JObject).Assembly, optional: false));
Assert.Equal(SR.Format(SR.Error_Missing_UserSecretsIdAttribute, typeof(JObject).Assembly.GetName().Name),
ex.Message);
}
[Fact]
- public void AddUserSecrets_DoesNotThrowsIfOptional()
+ public void AddUserSecrets_DoesNotThrowsIfOptionalByDefault()
{
var config = new ConfigurationBuilder()
- .AddUserSecrets(optional: true)
- .AddUserSecrets(typeof(List<>).Assembly, optional: true)
+ .AddUserSecrets()
+ .AddUserSecrets(typeof(List<>).Assembly)
.Build();
Assert.Empty(config.AsEnumerable());
diff --git a/src/libraries/Microsoft.VisualBasic.Core/Directory.Build.props b/src/libraries/Microsoft.VisualBasic.Core/Directory.Build.props
index fac4b55f49cd7d..e1dd239477f25a 100644
--- a/src/libraries/Microsoft.VisualBasic.Core/Directory.Build.props
+++ b/src/libraries/Microsoft.VisualBasic.Core/Directory.Build.props
@@ -1,7 +1,10 @@

- $([MSBuild]::Add($(MajorVersion), 5)).$(MinorVersion).0.0
+
+ $([MSBuild]::Add($(MajorVersion), 5))
+ $(MajorVersion).$(MinorVersion).0.0Microsofttrue
diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml
index 3349f853d44f50..38f8ffdbae7f02 100644
--- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml
+++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml
@@ -235,6 +235,9 @@
+
+
+
@@ -262,6 +265,9 @@
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index c651da612d53c4..6db3b715805a19 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -1550,7 +1550,7 @@
The buffer is not associated with this pool and may not be returned to it.
- Object is not a array with the same number of elements as the array to compare it to.
+ The object is not an array with the same number of elements as the array to compare it to.Object contains non-primitive or non-blittable data.
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index c3cb6737e980e0..4c4c1e0f436704 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -253,6 +253,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/ConstantExpectedAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/ConstantExpectedAttribute.cs
new file mode 100644
index 00000000000000..d5ff1fd7a8745a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/ConstantExpectedAttribute.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ ///
+ /// Indicates that the specified method parameter expects a constant.
+ ///
+ ///
+ /// This can be used to inform tooling that a constant should be used as an argument for the annotated parameter.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class ConstantExpectedAttribute : Attribute
+ {
+ ///
+ /// Indicates the minimum bound of the expected constant, inclusive.
+ ///
+ public object? Min { get; set; }
+ ///
+ /// Indicates the maximum bound of the expected constant, inclusive.
+ ///
+ public object? Max { get; set; }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs
index 3367fd0e29b9b3..a2d35f500e00c3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs
@@ -492,43 +492,45 @@ private static bool RemoveEmptyDirectory(string fullPath, bool topLevel = false,
if (Interop.Sys.RmDir(fullPath) < 0)
{
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- switch (errorInfo.Error)
+
+ if (errorInfo.Error == Interop.Error.ENOTEMPTY)
{
- case Interop.Error.EACCES:
- case Interop.Error.EPERM:
- case Interop.Error.EROFS:
- case Interop.Error.EISDIR:
- throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath)); // match Win32 exception
- case Interop.Error.ENOENT:
- // When we're recursing, don't throw for items that go missing.
- if (!topLevel)
- {
- return true;
- }
- goto default;
- case Interop.Error.ENOTDIR:
- // When the top-level path is a symlink to a directory, delete the link.
- // In other cases, throw because we expect path to be a real directory.
- if (topLevel)
- {
- if (!DirectoryExists(fullPath))
- {
- throw Interop.GetExceptionForIoErrno(Interop.Error.ENOENT.Info(), fullPath, isDirectory: true);
- }
+ if (!throwWhenNotEmpty)
+ {
+ return false;
+ }
+ }
+ else if (errorInfo.Error == Interop.Error.ENOENT)
+ {
+ // When we're recursing, don't throw for items that go missing.
+ if (!topLevel)
+ {
+ return true;
+ }
+ }
+ else if (DirectoryExists(fullPath, out Interop.ErrorInfo existErr))
+ {
+ // Top-level path is a symlink to a directory, delete the link.
+ if (topLevel && errorInfo.Error == Interop.Error.ENOTDIR)
+ {
+ DeleteFile(fullPath);
+ return true;
+ }
+ }
+ else if (existErr.Error == Interop.Error.ENOENT)
+ {
+ // Prefer throwing DirectoryNotFoundException over other exceptions.
+ errorInfo = existErr;
+ }
- DeleteFile(fullPath);
- return true;
- }
- goto default;
- case Interop.Error.ENOTEMPTY:
- if (!throwWhenNotEmpty)
- {
- return false;
- }
- goto default;
- default:
- throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, isDirectory: true);
+ if (errorInfo.Error == Interop.Error.EACCES ||
+ errorInfo.Error == Interop.Error.EPERM ||
+ errorInfo.Error == Interop.Error.EROFS)
+ {
+ throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath));
}
+
+ throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, isDirectory: true);
}
return true;
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs
index 76048da7b75c29..260c87b5fd9118 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.cs
@@ -62,13 +62,7 @@ public sealed partial class String : IComparable, IEnumerable, IConvertible, IEn
[DynamicDependency("Ctor(System.Char[])")]
public extern String(char[]? value);
-#pragma warning disable CA1822 // Mark members as static
-
- private
-#if !CORECLR
- static
-#endif
- string Ctor(char[]? value)
+ private static string Ctor(char[]? value)
{
if (value == null || value.Length == 0)
return Empty;
@@ -87,11 +81,7 @@ string Ctor(char[]? value)
[DynamicDependency("Ctor(System.Char[],System.Int32,System.Int32)")]
public extern String(char[] value, int startIndex, int length);
- private
-#if !CORECLR
- static
-#endif
- string Ctor(char[] value, int startIndex, int length)
+ private static string Ctor(char[] value, int startIndex, int length)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
@@ -123,11 +113,7 @@ string Ctor(char[] value, int startIndex, int length)
[DynamicDependency("Ctor(System.Char*)")]
public extern unsafe String(char* value);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(char* ptr)
+ private static unsafe string Ctor(char* ptr)
{
if (ptr == null)
return Empty;
@@ -151,11 +137,7 @@ unsafe string Ctor(char* ptr)
[DynamicDependency("Ctor(System.Char*,System.Int32,System.Int32)")]
public extern unsafe String(char* value, int startIndex, int length);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(char* ptr, int startIndex, int length)
+ private static unsafe string Ctor(char* ptr, int startIndex, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
@@ -190,11 +172,7 @@ unsafe string Ctor(char* ptr, int startIndex, int length)
[DynamicDependency("Ctor(System.SByte*)")]
public extern unsafe String(sbyte* value);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(sbyte* value)
+ private static unsafe string Ctor(sbyte* value)
{
byte* pb = (byte*)value;
if (pb == null)
@@ -210,11 +188,7 @@ unsafe string Ctor(sbyte* value)
[DynamicDependency("Ctor(System.SByte*,System.Int32,System.Int32)")]
public extern unsafe String(sbyte* value, int startIndex, int length);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(sbyte* value, int startIndex, int length)
+ private static unsafe string Ctor(sbyte* value, int startIndex, int length)
{
if (startIndex < 0)
throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
@@ -271,11 +245,7 @@ private static unsafe string CreateStringForSByteConstructor(byte* pb, int numBy
[DynamicDependency("Ctor(System.SByte*,System.Int32,System.Int32,System.Text.Encoding)")]
public extern unsafe String(sbyte* value, int startIndex, int length, Encoding enc);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding? enc)
+ private static unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding? enc)
{
if (enc == null)
return new string(value, startIndex, length);
@@ -307,11 +277,7 @@ unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding? enc)
[DynamicDependency("Ctor(System.Char,System.Int32)")]
public extern String(char c, int count);
- private
-#if !CORECLR
- static
-#endif
- string Ctor(char c, int count)
+ private static string Ctor(char c, int count)
{
if (count <= 0)
{
@@ -332,11 +298,7 @@ string Ctor(char c, int count)
[DynamicDependency("Ctor(System.ReadOnlySpan{System.Char})")]
public extern String(ReadOnlySpan value);
- private
-#if !CORECLR
- static
-#endif
- unsafe string Ctor(ReadOnlySpan value)
+ private static unsafe string Ctor(ReadOnlySpan value)
{
if (value.Length == 0)
return Empty;
@@ -346,8 +308,6 @@ unsafe string Ctor(ReadOnlySpan value)
return result;
}
-#pragma warning restore CA1822
-
public static string Create(int length, TState state, SpanAction action)
{
if (action == null)
diff --git a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
index 27bf37ab916fd0..fee9b78b71fa02 100644
--- a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
+++ b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs
@@ -2533,6 +2533,7 @@ public void AddCatchRegion(System.Reflection.Metadata.Ecma335.LabelHandle trySta
public void AddFaultRegion(System.Reflection.Metadata.Ecma335.LabelHandle tryStart, System.Reflection.Metadata.Ecma335.LabelHandle tryEnd, System.Reflection.Metadata.Ecma335.LabelHandle handlerStart, System.Reflection.Metadata.Ecma335.LabelHandle handlerEnd) { }
public void AddFilterRegion(System.Reflection.Metadata.Ecma335.LabelHandle tryStart, System.Reflection.Metadata.Ecma335.LabelHandle tryEnd, System.Reflection.Metadata.Ecma335.LabelHandle handlerStart, System.Reflection.Metadata.Ecma335.LabelHandle handlerEnd, System.Reflection.Metadata.Ecma335.LabelHandle filterStart) { }
public void AddFinallyRegion(System.Reflection.Metadata.Ecma335.LabelHandle tryStart, System.Reflection.Metadata.Ecma335.LabelHandle tryEnd, System.Reflection.Metadata.Ecma335.LabelHandle handlerStart, System.Reflection.Metadata.Ecma335.LabelHandle handlerEnd) { }
+ public void Clear() { }
}
public readonly partial struct CustomAttributeArrayTypeEncoder
{
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs
index bbe2809461b800..037ddf639c963c 100644
--- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs
+++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs
@@ -85,7 +85,10 @@ public ControlFlowBuilder()
_labels = ImmutableArray.CreateBuilder();
}
- internal void Clear()
+ ///
+ /// Clears the object's internal state, allowing the same instance to be reused.
+ ///
+ public void Clear()
{
_branches.Clear();
_labels.Clear();
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/Encoding/ControlFlowBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/Encoding/ControlFlowBuilderTests.cs
index d97701818feddd..93e3ab418286c9 100644
--- a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/Encoding/ControlFlowBuilderTests.cs
+++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/Encoding/ControlFlowBuilderTests.cs
@@ -418,5 +418,44 @@ public void Branch_LongInstruction_LongDistance()
(byte)ILOpCode.Ret
}, builder.ToArray());
}
+
+ [Fact]
+ public void Clear()
+ {
+ var cfb = new ControlFlowBuilder();
+
+ var il1 = GenerateSampleIL(cfb);
+ cfb.Clear();
+ var il2 = GenerateSampleIL(cfb);
+
+ AssertEx.Equal(il1, il2);
+
+ static byte[] GenerateSampleIL(ControlFlowBuilder cfb)
+ {
+ var code = new BlobBuilder();
+ var il = new InstructionEncoder(code, cfb);
+
+ var l1 = il.DefineLabel();
+ var l2 = il.DefineLabel();
+ var l3 = il.DefineLabel();
+ var l4 = il.DefineLabel();
+
+ il.MarkLabel(l1);
+ il.OpCode(ILOpCode.Nop);
+ il.Branch(ILOpCode.Br_s, l1);
+ il.MarkLabel(l2);
+ il.OpCode(ILOpCode.Nop);
+ il.MarkLabel(l3);
+ il.OpCode(ILOpCode.Nop);
+ il.MarkLabel(l4);
+
+ cfb.AddCatchRegion(l1, l2, l3, l4, MetadataTokens.TypeDefinitionHandle(1));
+
+ var builder = new BlobBuilder();
+ new MethodBodyStreamEncoder(builder).AddMethodBody(il);
+
+ return builder.ToArray();
+ }
+ }
}
}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 5da725f34f7277..f9462613a698b3 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -9041,6 +9041,12 @@ public sealed partial class AllowNullAttribute : System.Attribute
{
public AllowNullAttribute() { }
}
+ [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, Inherited=false)]
+ public sealed class ConstantExpectedAttribute : Attribute
+ {
+ public object? Min { get { throw null; } set { } }
+ public object? Max { get { throw null; } set { } }
+ }
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, Inherited=false)]
public sealed partial class DisallowNullAttribute : System.Attribute
{
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
index 45a57bda917c52..805d3610003be0 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
@@ -154,6 +154,7 @@
+
diff --git a/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/ConstantExpectedAttributeTests.cs b/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/ConstantExpectedAttributeTests.cs
new file mode 100644
index 00000000000000..390955277f71fb
--- /dev/null
+++ b/src/libraries/System.Runtime/tests/System/Diagnostics/CodeAnalysis/ConstantExpectedAttributeTests.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Diagnostics.CodeAnalysis.Tests
+{
+ public class ConstantExpectedAttributeTests
+ {
+ [Fact]
+ public void TestConstructor()
+ {
+ var attr = new ConstantExpectedAttribute();
+
+ Assert.Null(attr.Min);
+ Assert.Null(attr.Max);
+ }
+
+ [Theory]
+ [InlineData("https://dot.net")]
+ [InlineData("")]
+ [InlineData(10000)]
+ [InlineData(0.5f)]
+ [InlineData(null)]
+ public void TestSetMin(object min)
+ {
+ var attr = new ConstantExpectedAttribute
+ {
+ Min = min
+ };
+
+ Assert.Same(min, attr.Min);
+ }
+
+ [Theory]
+ [InlineData("https://dot.net")]
+ [InlineData("")]
+ [InlineData(10000)]
+ [InlineData(0.5f)]
+ [InlineData(null)]
+ public void TestSetMax(object max)
+ {
+ var attr = new ConstantExpectedAttribute
+ {
+ Max = max
+ };
+
+ Assert.Same(max, attr.Max);
+ }
+
+ [Theory]
+ [InlineData("", "https://dot.net")]
+ [InlineData(10000, 20000)]
+ [InlineData(0.5f, 2.0f)]
+ [InlineData(null, null)]
+ [InlineData(10, 0)]
+ [InlineData(10, "https://dot.net")]
+ public void TestSetMinAndMax(object min, object max)
+ {
+ var attr = new ConstantExpectedAttribute
+ {
+ Min = min,
+ Max = max
+ };
+
+ Assert.Same(min, attr.Min);
+ Assert.Same(max, attr.Max);
+ }
+ }
+}
diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj
index 9051d411c55028..6fcbb9d4e59475 100644
--- a/src/libraries/sendtohelixhelp.proj
+++ b/src/libraries/sendtohelixhelp.proj
@@ -32,6 +32,7 @@
<_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00
<_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:30:00
<_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00
+ <_workItemTimeout Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmDebuggerTests'">00:10:00
<_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and '$(Outerloop)' == 'true'">00:20:00
<_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == ''">00:15:00
<_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == ''">00:30:00
@@ -40,6 +41,7 @@
$(TestArchiveRuntimeFile)
+ $(TestArchiveTestsRoot)**/Wasm.Debugger.Tests.zip$(TestArchiveTestsRoot)**/*.zip$(Configuration)
@@ -54,11 +56,14 @@
$(TestRunNamePrefix)$(TestRunNamePrefixSuffix)-$(TestRunNamePrefix)$(Scenario)-
+ true$(RepositoryEngineeringDir)\testing\scenarios\BuildWasmAppsJobsList.txt
+ $(RepositoryEngineeringDir)\testing\scenarios\WasmDebuggerTestsJobsList.txt
+ true$(WaitForWorkItemCompletion)true
-
+
true$(RepoRoot)src\mono\wasm\emsdk\
@@ -66,6 +71,7 @@
truetruetrue
+ truedotnet-workloadsdk-no-workload
@@ -116,7 +122,7 @@
We also run some network tests to this server and so, we are running it on both HTTP and HTTPS.
For the HTTPS endpoint we need development SSL certificate.
-->
- true
+ truetrue
@@ -448,6 +454,10 @@
+
+
+
+
<_WorkItem Include="$(TestArchiveRoot)browseronly/**/*.zip" Condition="'$(TargetOS)' == 'Browser' and '$(Scenario)' == 'WasmTestOnBrowser'" />
-
+ %(Identity)$(HelixCommand)$(_workItemTimeout)
+
+
<_BuildWasmAppsPayloadArchive>@(_WorkItem)
+ <_WasmDebuggerTestsPayloadArchive>@(_WorkItem)
@@ -481,6 +495,19 @@
+
+
+ $(_WasmDebuggerTestsPayloadArchive)
+
+
+ set TEST_ARGS=--filter "FullyQualifiedName~%(Identity)^&Category!=windows-failing"
+ export TEST_ARGS="--filter FullyQualifiedName~%(Identity)&Category!=linux-failing"
+
+ $(HelixCommand)
+ $(_workItemTimeout)
+
+
+
dotnet exec $XHARNESS_CLI_PATH$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output
@@ -493,7 +520,7 @@
--browser-path=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe
-
+
<_RunOnlyWorkItem Include="$(TestArchiveRoot)runonly/**/*.Console.Sample.zip" />
@@ -526,6 +553,10 @@
ContinueOnError="true"
IgnoreExitCode="true"
IgnoreStandardErrorWarningFormat="true" />
+
+
+
+
-
+
+
diff --git a/src/mono/mono/mini/wasm_m2n_invoke.g.h b/src/mono/mono/mini/wasm_m2n_invoke.g.h
index 6f21c3f9378e76..9b3da495cf5338 100644
--- a/src/mono/mono/mini/wasm_m2n_invoke.g.h
+++ b/src/mono/mono/mini/wasm_m2n_invoke.g.h
@@ -1500,6 +1500,15 @@ wasm_invoke_viiiffiii (void *target_func, InterpMethodArguments *margs)
}
+static void
+wasm_invoke_viiifi (void *target_func, InterpMethodArguments *margs)
+{
+ typedef void (*T)(int arg_0, int arg_1, int arg_2, float arg_3, int arg_4);
+ T func = (T)target_func;
+ func ((int)(gssize)margs->iargs [0], (int)(gssize)margs->iargs [1], (int)(gssize)margs->iargs [2], *(float*)&margs->fargs [FIDX (0)], (int)(gssize)margs->iargs [3]);
+
+}
+
static void
wasm_invoke_viiifii (void *target_func, InterpMethodArguments *margs)
{
@@ -1655,6 +1664,46 @@ wasm_invoke_iill (void *target_func, InterpMethodArguments *margs)
}
+static void
+wasm_invoke_iiffff (void *target_func, InterpMethodArguments *margs)
+{
+ typedef int (*T)(int arg_0, float arg_1, float arg_2, float arg_3, float arg_4);
+ T func = (T)target_func;
+ int res = func ((int)(gssize)margs->iargs [0], *(float*)&margs->fargs [FIDX (0)], *(float*)&margs->fargs [FIDX (1)], *(float*)&margs->fargs [FIDX (2)], *(float*)&margs->fargs [FIDX (3)]);
+ *(int*)margs->retval = res;
+
+}
+
+static void
+wasm_invoke_iiiiidii (void *target_func, InterpMethodArguments *margs)
+{
+ typedef int (*T)(int arg_0, int arg_1, int arg_2, int arg_3, double arg_4, int arg_5, int arg_6);
+ T func = (T)target_func;
+ int res = func ((int)(gssize)margs->iargs [0], (int)(gssize)margs->iargs [1], (int)(gssize)margs->iargs [2], (int)(gssize)margs->iargs [3], margs->fargs [FIDX (0)], (int)(gssize)margs->iargs [4], (int)(gssize)margs->iargs [5]);
+ *(int*)margs->retval = res;
+
+}
+
+static void
+wasm_invoke_iiiiifiii (void *target_func, InterpMethodArguments *margs)
+{
+ typedef int (*T)(int arg_0, int arg_1, int arg_2, int arg_3, float arg_4, int arg_5, int arg_6, int arg_7);
+ T func = (T)target_func;
+ int res = func ((int)(gssize)margs->iargs [0], (int)(gssize)margs->iargs [1], (int)(gssize)margs->iargs [2], (int)(gssize)margs->iargs [3], *(float*)&margs->fargs [FIDX (0)], (int)(gssize)margs->iargs [4], (int)(gssize)margs->iargs [5], (int)(gssize)margs->iargs [6]);
+ *(int*)margs->retval = res;
+
+}
+
+static void
+wasm_invoke_liiii (void *target_func, InterpMethodArguments *margs)
+{
+ typedef gint64 (*T)(int arg_0, int arg_1, int arg_2, int arg_3);
+ T func = (T)target_func;
+ gint64 res = func ((int)(gssize)margs->iargs [0], (int)(gssize)margs->iargs [1], (int)(gssize)margs->iargs [2], (int)(gssize)margs->iargs [3]);
+ *(gint64*)margs->retval = res;
+
+}
+
static const char* interp_to_native_signatures [] = {
"DD",
"DDD",
@@ -1695,6 +1744,7 @@ static const char* interp_to_native_signatures [] = {
"IIF",
"IIFF",
"IIFFF",
+"IIFFFF",
"IIFFFFFF",
"IIFFFFFFFF",
"IIFFFFI",
@@ -1731,9 +1781,11 @@ static const char* interp_to_native_signatures [] = {
"IIIIFII",
"IIIIFIII",
"IIIII",
+"IIIIIDII",
"IIIIIF",
"IIIIIFFFFIIII",
"IIIIIFII",
+"IIIIIFIII",
"IIIIII",
"IIIIIIFFI",
"IIIIIIFII",
@@ -1764,6 +1816,7 @@ static const char* interp_to_native_signatures [] = {
"L",
"LI",
"LII",
+"LIIII",
"LIIIL",
"LIL",
"LILI",
@@ -1806,6 +1859,7 @@ static const char* interp_to_native_signatures [] = {
"VIIIF",
"VIIIFFII",
"VIIIFFIII",
+"VIIIFI",
"VIIIFII",
"VIIIFIII",
"VIIII",
@@ -1868,6 +1922,7 @@ wasm_invoke_ii,
wasm_invoke_iif,
wasm_invoke_iiff,
wasm_invoke_iifff,
+wasm_invoke_iiffff,
wasm_invoke_iiffffff,
wasm_invoke_iiffffffff,
wasm_invoke_iiffffi,
@@ -1904,9 +1959,11 @@ wasm_invoke_iiiifi,
wasm_invoke_iiiifii,
wasm_invoke_iiiifiii,
wasm_invoke_iiiii,
+wasm_invoke_iiiiidii,
wasm_invoke_iiiiif,
wasm_invoke_iiiiiffffiiii,
wasm_invoke_iiiiifii,
+wasm_invoke_iiiiifiii,
wasm_invoke_iiiiii,
wasm_invoke_iiiiiiffi,
wasm_invoke_iiiiiifii,
@@ -1937,6 +1994,7 @@ wasm_invoke_ili,
wasm_invoke_l,
wasm_invoke_li,
wasm_invoke_lii,
+wasm_invoke_liiii,
wasm_invoke_liiil,
wasm_invoke_lil,
wasm_invoke_lili,
@@ -1979,6 +2037,7 @@ wasm_invoke_viii,
wasm_invoke_viiif,
wasm_invoke_viiiffii,
wasm_invoke_viiiffiii,
+wasm_invoke_viiifi,
wasm_invoke_viiifii,
wasm_invoke_viiifiii,
wasm_invoke_viiii,
diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile
index 04828bba7240ce..d3f9b3bf08cb2a 100644
--- a/src/mono/wasm/Makefile
+++ b/src/mono/wasm/Makefile
@@ -23,6 +23,7 @@ SYSTEM_NATIVE_LIBDIR?=$(TOP)/src/native/libs/System.Native
_MSBUILD_WASM_BUILD_ARGS=/p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=$(CONFIG)
XHARNESS_BROWSER?=chrome
EMCC_DEFAULT_RSP=$(NATIVE_BIN_DIR)/src/emcc-default.rsp
+HELIX_TARGET_QUEUE?=Ubuntu.1804.Amd64.Open
all: build-native icu-files source-files header-files
@@ -164,6 +165,32 @@ run-build-tests:
run-browser-tests-%:
PATH="$(GECKODRIVER):$(CHROMEDRIVER):$(PATH)" XHARNESS_COMMAND="test-browser --browser=$(XHARNESS_BROWSER)" $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS)
+build-debugger-tests-helix:
+ $(DOTNET) build -restore -bl:$(TOP)/artifacts/log/$(CONFIG)/Wasm.Debugger.Tests.binlog \
+ /p:ContinuousIntegrationBuild=true /p:ArchiveTests=true \
+ $(TOP)/src/tests/BuildWasmApps/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj \
+ $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS)
+
+submit-debugger-tests-helix: build-debugger-tests-helix
+ EMSDK_PATH=$(EMSDK_PATH) BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \
+ $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \
+ /p:TestRunNamePrefixSuffix=WasmDebugger /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \
+ /bl:$(TOP)/artifacts/log/$(CONFIG)/SendToHelix.binlog -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \
+ /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \
+ /p:_Scenarios=wasmdebuggertests \
+ $(_MSBUILD_WASM_BUILD_ARGS) \
+ $(MSBUILD_ARGS)
+
+submit-tests-helix:
+ echo "\n** This will submit all the available test zip files to helix **\n"
+ EMSDK_PATH=$(EMSDK_PATH) BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \
+ $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \
+ /p:TestRunNamePrefixSuffix=WasmTests /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \
+ /bl:$(TOP)/artifacts/log/$(CONFIG)/SendToHelix.binlog -v:n -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \
+ /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \
+ $(_MSBUILD_WASM_BUILD_ARGS) \
+ $(MSBUILD_ARGS)
+
run-debugger-tests:
if [ ! -z "$(TEST_FILTER)" ]; then \
$(DOTNET) test $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS) --filter FullyQualifiedName~$(TEST_FILTER) $(TEST_ARGS); \
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index 1185fc11a562e0..1df620145bea36 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -207,9 +207,13 @@
<_ParsedRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' != ''">$([System.IO.Path]::GetDirectoryName($(_WasmRuntimeConfigFilePath)))\runtimeconfig.bin
-
-
+
+
+
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
index f5dc5ecca471b9..074a953b694ae8 100644
--- a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
+++ b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
@@ -160,7 +160,11 @@ async Task ConnectProxy(HttpContext context)
try
{
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
- builder.AddSimpleConsole(options => options.SingleLine = true)
+ builder.AddSimpleConsole(options =>
+ {
+ options.SingleLine = true;
+ options.TimestampFormat = "[HH:mm:ss] ";
+ })
.AddFilter(null, LogLevel.Information)
);
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
index e5579df8f86487..6fb9b309d88e28 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
@@ -824,14 +824,14 @@ internal void AddMethod(MethodInfo mi)
return (start.StartLocation.Line, start.StartLocation.Column, end.EndLocation.Line, end.EndLocation.Column);
}
- private static async Task GetDataAsync(Uri uri, CancellationToken token)
+ private async Task GetDataAsync(Uri uri, CancellationToken token)
{
var mem = new MemoryStream();
try
{
if (uri.IsFile && File.Exists(uri.LocalPath))
{
- using (FileStream file = File.Open(uri.LocalPath, FileMode.Open))
+ using (FileStream file = File.Open(SourceUri.LocalPath, FileMode.Open))
{
await file.CopyToAsync(mem, token).ConfigureAwait(false);
mem.Position = 0;
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
index 05d3d6cccbf66d..82057f4c75bd39 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs
@@ -4,13 +4,14 @@
using System;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.WebAssembly.Diagnostics;
using Newtonsoft.Json.Linq;
using Xunit;
namespace DebuggerTests
{
-
+ // https://github.com/dotnet/runtime/issues/62661
+ [Trait("Category", "windows-failing")]
+ [Trait("Category", "linux-failing")]
public class ArrayTests : DebuggerTestBase
{
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs
index 0601552db6b606..7a5111b3647122 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssignmentTests.cs
@@ -46,19 +46,21 @@ async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDe
// 1) check un-assigned variables
await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-assignment-test.cs", -1, -1, "TestedMethod",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
Assert.Equal(2, locals.Count());
Check(locals, "r", checkDefault);
+ await Task.CompletedTask;
}
);
// 2) check assigned variables
await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-assignment-test.cs", -1, -1, "TestedMethod", times: 3,
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
Assert.Equal(2, locals.Count());
Check(locals, "r", checkValue);
+ await Task.CompletedTask;
}
);
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
index f3bf3c8ecb4a6f..b80a8f429b4827 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
@@ -257,38 +257,45 @@ await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method_async('[debugger-test] UserBreak:BreakOnDebuggerBreakCommand'); }, 1);",
"dotnet://debugger-test.dll/debugger-test2.cs", 58, 8,
"BreakOnDebuggerBreakCommand",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test2.cs", 59, 8, "BreakOnDebuggerBreakCommand",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test2.cs", 60, 8, "BreakOnDebuggerBreakCommand",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 20);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test2.cs", 61, 8, "BreakOnDebuggerBreakCommand",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 50);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test2.cs", 62, 4, "BreakOnDebuggerBreakCommand",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 100);
+ await Task.CompletedTask;
}
);
}
[Fact]
+ [Trait("Category", "windows-failing")] // https://github.com/dotnet/runtime/issues/62823
+ [Trait("Category", "linux-failing")] // https://github.com/dotnet/runtime/issues/62823
public async Task BreakpointInAssemblyUsingTypeFromAnotherAssembly_BothDynamicallyLoaded()
{
int line = 7;
@@ -370,24 +377,27 @@ public async Task DebugHotReloadMethodAddBreakpoint()
CheckBool(locals, "c", true);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 31, 12, "StaticMethod3",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "d", 10);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 32, 12, "StaticMethod3",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "d", 10);
CheckNumber(locals, "e", 20);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 33, 8, "StaticMethod3",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "d", 10);
CheckNumber(locals, "e", 20);
CheckNumber(locals, "f", 50);
+ await Task.CompletedTask;
}
);
}
@@ -408,37 +418,42 @@ public async Task DebugHotReloadMethodEmpty()
pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 38, 12, "StaticMethod4");
locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value());
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 39, 12, "StaticMethod4",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 40, 12, "StaticMethod4",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
CheckNumber(locals, "b", 20);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 41, 12, "StaticMethod4",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
CheckNumber(locals, "b", 20);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 42, 12, "StaticMethod4",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
CheckNumber(locals, "b", 20);
+ await Task.CompletedTask;
}
);
await StepAndCheck(StepKind.Over, "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 43, 8, "StaticMethod4",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
CheckNumber(locals, "b", 20);
+ await Task.CompletedTask;
}
);
pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 38, 8, "StaticMethod4");
@@ -456,9 +471,10 @@ await EvaluateAndCheck(
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 3);
+ await Task.CompletedTask;
}
);
@@ -480,9 +496,10 @@ await EvaluateAndCheck(
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 0);
+ await Task.CompletedTask;
}
);
@@ -491,9 +508,10 @@ await SendCommandAndCheck(null, "Debugger.resume",
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 3);
+ await Task.CompletedTask;
});
await SendCommandAndCheck(null, "Debugger.resume",
@@ -501,9 +519,10 @@ await SendCommandAndCheck(null, "Debugger.resume",
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 6);
+ await Task.CompletedTask;
});
await SendCommandAndCheck(null, "Debugger.resume",
@@ -511,9 +530,10 @@ await SendCommandAndCheck(null, "Debugger.resume",
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 9);
+ await Task.CompletedTask;
});
await SendCommandAndCheck(null, "Debugger.resume",
@@ -547,9 +567,10 @@ await EvaluateAndCheck(
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 0);
+ await Task.CompletedTask;
}
);
@@ -558,9 +579,10 @@ await SendCommandAndCheck(null, "Debugger.resume",
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 1);
+ await Task.CompletedTask;
});
await SendCommandAndCheck(null, "Debugger.resume",
@@ -568,9 +590,10 @@ await SendCommandAndCheck(null, "Debugger.resume",
bp_conditional.Value["locations"][0]["lineNumber"].Value(),
bp_conditional.Value["locations"][0]["columnNumber"].Value(),
"LoopToBreak",
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
CheckNumber(locals, "i", 2);
+ await Task.CompletedTask;
});
}
@@ -642,7 +665,7 @@ await EvaluateAndCheck(
}
);
}
-
+
[Fact]
public async Task DebuggerAttributeNoStopInDebuggerHidden()
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs
index 2d3707fec1d8d8..8115604204a81d 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs
@@ -8,7 +8,7 @@
namespace DebuggerTests
{
- public class DateTimeList : DebuggerTestBase
+ public class DateTimeTests : DebuggerTestBase
{
[Theory]
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
index 1329e284eb066a..fbc176dc2ca5e3 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
@@ -40,17 +40,30 @@ protected static string DebuggerTestAppPath
static protected string FindTestPath()
{
- var asm_dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string test_app_path = Environment.GetEnvironmentVariable("DEBUGGER_TEST_PATH");
+
+ if (string.IsNullOrEmpty(test_app_path))
+ {
+ var asm_dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
#if DEBUG
- var config="Debug";
+ var config="Debug";
#else
- var config="Release";
-#endif
- var test_app_path = Path.Combine(asm_dir, "..", "..", "..", "debugger-test", config, "publish");
+ var config="Release";
+#endif
+ test_app_path = Path.Combine(asm_dir, "..", "..", "..", "debugger-test", config);
+
+ if (string.IsNullOrEmpty(test_app_path))
+ throw new Exception("Could not figure out debugger-test app path from the 'DEBUGGER_TEST_PATH' " +
+ $"environment variable, or based on the test suite location ({asm_dir})");
+ }
+
+ if (!string.IsNullOrEmpty(test_app_path))
+ test_app_path = Path.Combine(test_app_path, "AppBundle");
+
if (File.Exists(Path.Combine(test_app_path, "debugger-driver.html")))
return test_app_path;
- throw new Exception($"Could not figure out debugger-test app path ({test_app_path}) based on the test suite location ({asm_dir})");
+ throw new Exception($"Cannot find 'debugger-driver.html' in {test_app_path}");
}
static string[] PROBE_LIST = {
@@ -63,21 +76,32 @@ static protected string FindTestPath()
};
static string chrome_path;
- static string FindChromePath()
+ static string GetChromePath()
{
- if (chrome_path != null)
- return chrome_path;
+ if (string.IsNullOrEmpty(chrome_path))
+ {
+ chrome_path = FindChromePath();
+ if (!string.IsNullOrEmpty(chrome_path))
+ Console.WriteLine ($"** Using chrome from {chrome_path}");
+ else
+ throw new Exception("Could not find an installed Chrome to use");
+ }
- foreach (var s in PROBE_LIST)
+ return chrome_path;
+
+ string FindChromePath()
{
- if (File.Exists(s))
+ string chrome_path_env_var = Environment.GetEnvironmentVariable("CHROME_PATH_FOR_DEBUGGER_TESTS");
+ if (!string.IsNullOrEmpty(chrome_path_env_var))
{
- chrome_path = s;
- Console.WriteLine($"Using chrome path: ${s}");
- return s;
+ if (File.Exists(chrome_path_env_var))
+ return chrome_path_env_var;
+
+ Console.WriteLine ($"warning: Could not find CHROME_PATH_FOR_DEBUGGER_TESTS={chrome_path_env_var}");
}
+
+ return PROBE_LIST.FirstOrDefault(p => File.Exists(p));
}
- throw new Exception("Could not find an installed Chrome to use");
}
public DebuggerTestBase(string driver = "debugger-driver.html")
@@ -90,7 +114,7 @@ public DebuggerTestBase(string driver = "debugger-driver.html")
cli = insp.Client;
scripts = SubscribeToScripts(insp);
- startTask = TestHarnessProxy.Start(FindChromePath(), DebuggerTestAppPath, driver);
+ startTask = TestHarnessProxy.Start(GetChromePath(), DebuggerTestAppPath, driver);
}
public virtual async Task InitializeAsync()
@@ -148,7 +172,7 @@ internal Dictionary SubscribeToScripts(Inspector insp)
}
internal async Task CheckInspectLocalsAtBreakpointSite(string url_key, int line, int column, string function_name, string eval_expression,
- Action test_fn = null, Func wait_for_event_fn = null, bool use_cfo = false)
+ Func test_fn = null, Func wait_for_event_fn = null, bool use_cfo = false)
{
UseCallFunctionOnBeforeGetProperties = use_cfo;
@@ -171,10 +195,10 @@ await EvaluateAndCheck(
else
await Task.CompletedTask;
},
- locals_fn: (locals) =>
+ locals_fn: async (locals) =>
{
if (test_fn != null)
- test_fn(locals);
+ await test_fn(locals);
}
);
}
@@ -436,7 +460,7 @@ internal async Task InvokeGetter(JToken obj, object arguments, string fn
}
internal async Task StepAndCheck(StepKind kind, string script_loc, int line, int column, string function_name,
- Func wait_for_event_fn = null, Action locals_fn = null, int times = 1)
+ Func wait_for_event_fn = null, Func locals_fn = null, int times = 1)
{
string method = (kind == StepKind.Resume ? "Debugger.resume" : $"Debugger.step{kind}");
for (int i = 0; i < times - 1; i++)
@@ -451,15 +475,17 @@ internal async Task StepAndCheck(StepKind kind, string script_loc, int
locals_fn: locals_fn);
}
- internal async Task EvaluateAndCheck(string expression, string script_loc, int line, int column, string function_name,
- Func wait_for_event_fn = null, Action locals_fn = null) => await SendCommandAndCheck(
- JObject.FromObject(new { expression = expression }),
- "Runtime.evaluate", script_loc, line, column, function_name,
- wait_for_event_fn: wait_for_event_fn,
- locals_fn: locals_fn);
+ internal async Task EvaluateAndCheck(
+ string expression, string script_loc, int line, int column, string function_name,
+ Func wait_for_event_fn = null, Func locals_fn = null)
+ => await SendCommandAndCheck(
+ JObject.FromObject(new { expression = expression }),
+ "Runtime.evaluate", script_loc, line, column, function_name,
+ wait_for_event_fn: wait_for_event_fn,
+ locals_fn: locals_fn);
internal async Task SendCommandAndCheck(JObject args, string method, string script_loc, int line, int column, string function_name,
- Func wait_for_event_fn = null, Action locals_fn = null, string waitForEvent = Inspector.PAUSE)
+ Func wait_for_event_fn = null, Func locals_fn = null, string waitForEvent = Inspector.PAUSE)
{
var res = await cli.SendCommand(method, args, token);
if (!res.IsOk)
@@ -486,7 +512,7 @@ internal async Task SendCommandAndCheck(JObject args, string method, st
var locals = await GetProperties(wait_res["callFrames"][0]["callFrameId"].Value());
try
{
- locals_fn(locals);
+ await locals_fn(locals);
}
catch (System.AggregateException ex)
{
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
index a46fd059b01fe9..90e236ceb5b520 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
@@ -16,7 +16,18 @@
-
+
+
+
+
+ <_FilesToCopy Include="$(OutputPath)\**\*" TargetPath="DebuggerTestSuite" />
+ <_FilesToCopy Include="$(ArtifactsBinDir)debugger-test\Debug\**\*" TargetPath="debugger-test" />
+
+
+
+
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs
index 8919f4a6ee1e40..e1ed977ae52e96 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs
@@ -235,6 +235,7 @@ await CheckValue(pause_location["data"], JObject.FromObject(new
[Theory]
[InlineData("[debugger-test] DebuggerTests.ExceptionTestsClassDefault:TestExceptions", "System.Exception", 76)]
[InlineData("[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions", "DebuggerTests.CustomException", 28)]
+ [Trait("Category", "linux-failing")] // https://github.com/dotnet/runtime/issues/62666
public async Task ExceptionTestAllWithReload(string entry_method_name, string class_name, int line_number)
{
var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs";
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs
index ef36ace6624a16..865eaa8c630f5e 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs
@@ -36,8 +36,13 @@ public async Task BrowserCrash() => await Assert.ThrowsAsync
await SendCommandAndCheck(null, "Browser.crash", null, -1, -1, null));
[Fact]
- public async Task BrowserClose() => await Assert.ThrowsAsync(async () =>
- await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null));
+ public async Task BrowserClose()
+ {
+ ArgumentException ae = await Assert.ThrowsAsync(async () =>
+ await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null));
+ Assert.Contains("Inspector.detached", ae.Message);
+ Assert.Contains("target_close", ae.Message);
+ }
[Fact]
public async Task InspectorWaitForAfterMessageAlreadyReceived()
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
index 826d860c21e4d4..616349e3385317 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
@@ -42,7 +42,11 @@ public Inspector()
Token = _cancellationTokenSource.Token;
_loggerFactory = LoggerFactory.Create(builder =>
- builder.AddSimpleConsole(options => options.SingleLine = true)
+ builder.AddSimpleConsole(options =>
+ {
+ options.SingleLine = true;
+ options.TimestampFormat = "[HH:mm:ss] ";
+ })
.AddFilter(null, LogLevel.Trace));
Client = new InspectorClient(_loggerFactory.CreateLogger());
@@ -175,6 +179,7 @@ async Task OnMessage(string method, JObject args, CancellationToken token)
}
else if (fail)
{
+ args["__forMethod"] = method;
FailAllWaiters(new ArgumentException(args.ToString()));
}
}
@@ -198,7 +203,7 @@ public async Task OpenSessionAsync(Func
+ test_fn: async (locals) =>
{
CheckNumber(locals, "a", 10);
CheckNumber(locals, "b", 20);
CheckNumber(locals, "c", 30);
CheckNumber(locals, "d", 0);
CheckNumber(locals, "e", 0);
+ await Task.CompletedTask;
}
);
@@ -81,10 +81,11 @@ public async Task InspectPrimitiveTypeLocalsAtBreakpointSite() =>
await CheckInspectLocalsAtBreakpointSite(
"dotnet://debugger-test.dll/debugger-test.cs", 154, 8, "PrimitiveTypesTest",
"window.setTimeout(function() { invoke_static_method ('[debugger-test] Math:PrimitiveTypesTest'); }, 1);",
- test_fn: (locals) =>
+ test_fn: async (locals) =>
{
CheckSymbol(locals, "c0", "8364 '€'");
CheckSymbol(locals, "c1", "65 'A'");
+ await Task.CompletedTask;
}
);
@@ -94,7 +95,7 @@ await CheckInspectLocalsAtBreakpointSite(
"dotnet://debugger-test.dll/debugger-test2.cs", 50, 8, "Types",
"window.setTimeout(function() { invoke_static_method (\"[debugger-test] Fancy:Types\")(); }, 1);",
use_cfo: false,
- test_fn: (locals) =>
+ test_fn: async (locals) =>
{
CheckNumber(locals, "dPI", Math.PI);
CheckNumber(locals, "fPI", (float)Math.PI);
@@ -116,6 +117,7 @@ await CheckInspectLocalsAtBreakpointSite(
CheckNumber(locals, "sMin", short.MinValue);
CheckNumber(locals, "usMin", ushort.MinValue);
CheckNumber(locals, "usMax", ushort.MaxValue);
+ await Task.CompletedTask;
}
);
@@ -199,7 +201,7 @@ await CheckInspectLocalsAtBreakpointSite(
"dotnet://debugger-test.dll/debugger-test.cs", 74, 8, "GenericTypesTest",
"window.setTimeout(function() { invoke_generic_types_test (); }, 1);",
use_cfo: use_cfo,
- test_fn: (locals) =>
+ test_fn: async (locals) =>
{
CheckObject(locals, "list", "System.Collections.Generic.Dictionary