diff --git a/docs/area-owners.md b/docs/area-owners.md
index 4188ea7d982a6..740404f771aa2 100644
--- a/docs/area-owners.md
+++ b/docs/area-owners.md
@@ -79,6 +79,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue
| area-System.Diagnostics-mono | @lewing | @thaystg @radical | |
| area-System.Diagnostics.Activity | @tommcdon | @tarekgh | |
| area-System.Diagnostics.EventLog | @ericstj | @Anipik @ViktorHofer | |
+| area-System.Diagnostics.Metric | @tommcdon | @noahfalk | |
| area-System.Diagnostics.PerformanceCounter | @ericstj | @Anipik @ViktorHofer | |
| area-System.Diagnostics.Process | @jeffhandley | @adamsitnik @carlossanlop @jozkee | |
| area-System.Diagnostics.Tracing | @tommcdon | @noahfalk @tommcdon @Anipik @ViktorHofer @tarekgh | Included:
- System.Diagnostics.DiagnosticSource
- System.Diagnostics.TraceSource
|
diff --git a/docs/design/mono/wasm-aot.md b/docs/design/mono/wasm-aot.md
new file mode 100644
index 0000000000000..667d124ed9c6e
--- /dev/null
+++ b/docs/design/mono/wasm-aot.md
@@ -0,0 +1,66 @@
+# WebAssembly AOT code generation
+
+## Basic operation
+
+The LLVM backend of the Mono JIT is used to generate an llvm .bc file for each assembly, then the .bc files are
+compiled to webassembly using emscripten, then the resulting wasm files are linked into the final app. The 'bitcode'/'llvmonly'
+variant of the LLVM backend is used since webassembly doesn't support inline assembly etc.
+
+## GC Support
+
+On wasm, the execution stack is not stored in linear memory, so its not possible to scan it for GC references. However, there
+is an additional C stack which stores variables whose addresses are taken. Variables which hold GC references are marked as
+'volatile' in the llvm backend, forcing llvm to spill those to the C stack so they can be scanned.
+
+## Interpreter support
+
+Its possible for AOTed and interpreted code to interop, this is called mixed mode.
+For the AOT -> interpreter case, every call from AOTed code which might end up in the interpreter is
+emitted as an indirect call. When the callee is not found, a wrapper function is used which
+packages up the arguments into an array and passes control to the interpreter.
+For the interpreter -> AOT case, and similar wrapper function is used which receives the
+arguments and a return value pointer from the interpreter in an array, and calls the
+AOTed code. There is usually one aot->interp and interp->aot wrapper for each signature, with
+some sharing. These wrappers are generated by the AOT compiler when the 'interp' aot option
+is used.
+
+## Null checks
+
+Since wasm has no signal support, we generate explicit null checks.
+
+## Issues
+
+The generated code is in general much bigger than the code generated on ios etc. Some of the
+current issues are described below.
+
+### Function pointers
+
+The runtime needs to be able to do a IL method -> wasm function lookup. To do this, every
+AOT image includes a table mapping from a method index to wasm functions. This means that
+every generated AOT method has its address taken, which severely limits the interprocedural
+optimizations that LLVM can do, since it cannot determine the set of callers for a function.
+This means that it cannot remove functions corresponding to unused IL methods, cannot
+specialize functions for constant/nonnull arguments, etc.
+The dotnet linker includes some support for adding a [DisablePrivateReflection] attribute to
+methods which cannot be called using reflection, and the AOT compiler could use this
+to avoid generating function pointers for methods which are not called from outside the
+AOT image. This is not enabled right now because the linker support is not complete.
+
+### Null checks
+
+The explicit null checking code adds a lot of size overhead since null checks are very common.
+
+### Virtual calls
+
+Vtable slots are lazily initialized on the first call, i.e. every virtual call looks like this:
+```C
+vt_entry = vtable [slot];
+if (vt_entry == null)
+ vt_entry = init_vt_entry ();
+```
+
+### GC overhead
+
+Since GC variables are marked as volatile and stored on the C stack, they are loaded/stored on every access,
+even if there is no GC safe point between the accesses. Instead, they should only be loaded/stored around
+GC safe points.
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 2d16853231758..cfb593d70b54d 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -85,6 +85,7 @@ The PR that reveals the implementation of the `
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 2389066e4b33e..58a2c18606c85 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,190 +1,190 @@
-
+
https://github.com/dotnet/icu
- e7626ad8c04b150de635f920b5e8dede0aafaf73
+ 1782cd21854f8cb2b60355f4773714a8e0130696
https://github.com/dotnet/msquic
d7db669b70f4dd67ec001c192f9809c218cab88b
-
+
https://github.com/dotnet/emsdk
- 5c9145289bd4d4e14b18a544dda60a185f66f688
+ 8a6313ffbdbef76fd01e32e37c802b57945531d3
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
https://github.com/microsoft/vstest
140434f7109d357d0158ade9e5164a4861513965
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
-
+
https://github.com/dotnet/llvm-project
- a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
+ 97d42b6f43e31449758c353cfbaf83322941611b
https://github.com/dotnet/runtime
38017c3935de95d0335bac04f4901ddfc2718656
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/dotnet/runtime
- 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
+ 98b7ed1a3b0543a31b5a0f9069cf44cb70c9230c
-
+
https://github.com/mono/linker
- 35a1c74d6a0dbd115bf079dc986cea59cdb01430
+ 664e78edc72dd0a48e6f55e352051b6ba61bba9a
https://github.com/dotnet/xharness
@@ -194,33 +194,33 @@
https://github.com/dotnet/xharness
c6d444eaf7e95339589ceef371cbef0a90a4add5
-
+
https://github.com/dotnet/arcade
- 286d98094b830b8dad769542b2669cb1b75f7097
+ 55262f114b0c1b82f0b081bca0d919b657ba24c5
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- a89f052e97fec59a2d0148c08d3b4801567ec200
+ a83e4392b6d3f377239726eedc19b6dc85b85496
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- a89f052e97fec59a2d0148c08d3b4801567ec200
+ a83e4392b6d3f377239726eedc19b6dc85b85496
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- a89f052e97fec59a2d0148c08d3b4801567ec200
+ a83e4392b6d3f377239726eedc19b6dc85b85496
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- a89f052e97fec59a2d0148c08d3b4801567ec200
+ a83e4392b6d3f377239726eedc19b6dc85b85496
-
+
https://github.com/dotnet/hotreload-utils
- 3960ef9a8980181e840b5c1d64ed0b234711e850
+ 33219d2b3fbc957e05f8e52a33363cf9b858bb08
-
+
https://github.com/dotnet/runtime-assets
- c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
+ 3db3f0a34f73db72e5b918ad22e1fbe9f1c5c4da
https://github.com/dotnet/roslyn-analyzers
diff --git a/eng/Versions.props b/eng/Versions.props
index 7be0ee173f2f3..1e7be3b4cb980 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -17,8 +17,8 @@
release
true
-
- 4.0.0-2.21323.11
+
+ 4.0.0-3.21362.7
true
false
false
@@ -52,28 +52,28 @@
3.10.0
6.0.0-rc1.21356.1
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 2.5.1-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
- 6.0.0-beta.21357.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 2.5.1-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
+ 6.0.0-beta.21359.3
6.0.0-preview.1.102
6.0.0-alpha.1.20612.4
- 6.0.0-preview.7.21355.1
- 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21361.10
+ 6.0.0-preview.7.21361.10
3.1.0
- 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21361.10
5.0.0
4.3.0
@@ -107,27 +107,27 @@
5.0.0
5.0.0
4.8.1
- 6.0.0-preview.7.21355.1
- 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21361.10
+ 6.0.0-preview.7.21361.10
4.5.4
4.5.0
- 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21361.10
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
- 6.0.0-beta.21356.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
+ 6.0.0-beta.21358.1
- 1.0.0-prerelease.21357.3
- 1.0.0-prerelease.21357.3
- 1.0.0-prerelease.21357.3
- 1.0.0-prerelease.21357.3
+ 1.0.0-prerelease.21362.2
+ 1.0.0-prerelease.21362.2
+ 1.0.0-prerelease.21362.2
+ 1.0.0-prerelease.21362.2
16.9.0-beta1.21055.5
2.0.0-beta1.20253.1
@@ -153,7 +153,7 @@
16.9.0-preview-20201201-01
1.0.0-prerelease.21357.4
1.0.0-prerelease.21357.4
- 1.0.1-alpha.0.21355.1
+ 1.0.1-alpha.0.21362.1
2.4.1
2.4.2
1.3.0
@@ -164,23 +164,23 @@
5.0.0-preview-20201009.2
- 6.0.100-preview.6.21357.1
+ 6.0.100-preview.6.21362.3
$(MicrosoftNETILLinkTasksVersion)
- 6.0.0-preview.7.21328.1
+ 6.0.0-preview.7.21362.1
6.0.0-preview.7.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
- 11.1.0-alpha.1.21357.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
+ 11.1.0-alpha.1.21362.1
- 6.0.0-preview.7.21358.1
+ 6.0.0-preview.7.21363.1
$(MicrosoftNETWorkloadEmscriptenManifest60100Version)
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 4b25520324931..2df0909937d10 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -42,7 +42,7 @@
[bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true }
# Enable repos to use a particular version of the on-line dotnet-install scripts.
-# default URL: https://dot.net/v1/dotnet-install.ps1
+# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1
[string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' }
# True to use global NuGet cache instead of restoring packages to repository-local directory.
@@ -223,7 +223,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) {
if (!(Test-Path $installScript)) {
Create-Directory $dotnetRoot
$ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit
- $uri = "https://dot.net/$dotnetInstallScriptVersion/dotnet-install.ps1"
+ $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1"
Retry({
Write-Host "GET $uri"
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index 05ca99c6b2813..828119be411b3 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -54,7 +54,7 @@ warn_as_error=${warn_as_error:-true}
use_installed_dotnet_cli=${use_installed_dotnet_cli:-true}
# Enable repos to use a particular version of the on-line dotnet-install scripts.
-# default URL: https://dot.net/v1/dotnet-install.sh
+# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh
dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'}
# True to use global NuGet cache instead of restoring packages to repository-local directory.
@@ -262,7 +262,7 @@ function with_retries {
function GetDotNetInstallScript {
local root=$1
local install_script="$root/dotnet-install.sh"
- local install_script_url="https://dot.net/$dotnetInstallScriptVersion/dotnet-install.sh"
+ local install_script_url="https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh"
if [[ ! -a "$install_script" ]]; then
mkdir -p "$root"
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index 7d33d26edb012..75d6cbd212217 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -57,7 +57,7 @@ jobs:
- (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759
- RedHat.7.Amd64.Open
- SLES.15.Amd64.Open
- - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20200512010618-efb9f14
+ - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408
- (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623
- (Debian.10.Amd64.Open)ubuntu.1604.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')) }}:
@@ -70,7 +70,7 @@ jobs:
- SLES.12.Amd64.Open
- SLES.15.Amd64.Open
- (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7
- - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20200512010618-efb9f14
+ - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408
- (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623
- (Debian.10.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006
- ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}:
@@ -80,7 +80,7 @@ jobs:
- Ubuntu.1604.Amd64.Open
- Ubuntu.1804.Amd64.Open
- SLES.15.Amd64.Open
- - (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7
+ - (Fedora.32.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-32-helix-20210710031120-870c408
- ${{ if or(eq(parameters.jobParameters.interpreter, 'true'), eq(parameters.jobParameters.isSingleFile, true)) }}:
# Limiting interp runs as we don't need as much coverage.
- Debian.9.Amd64.Open
diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets
index f80b26fb98445..0f44b205eb6a0 100644
--- a/eng/targetingpacks.targets
+++ b/eng/targetingpacks.targets
@@ -11,6 +11,7 @@
$(MicrosoftNetCoreAppFrameworkName)
+ true
diff --git a/global.json b/global.json
index 99807111962c2..e25c47c09f713 100644
--- a/global.json
+++ b/global.json
@@ -12,13 +12,13 @@
"python3": "3.7.1"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21357.3",
+ "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21359.3",
"Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4",
- "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21357.3",
- "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21357.3",
- "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21357.3",
+ "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21359.3",
+ "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21359.3",
+ "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21359.3",
"Microsoft.Build.NoTargets": "3.0.4",
"Microsoft.Build.Traversal": "3.0.23",
- "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21355.1"
+ "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21361.10"
}
}
diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt
index 78aa969473525..b4a4859342702 100644
--- a/src/coreclr/CMakeLists.txt
+++ b/src/coreclr/CMakeLists.txt
@@ -119,6 +119,8 @@ add_subdirectory(pal/prebuilt/inc)
add_subdirectory(debug/debug-pal)
+add_subdirectory(minipal)
+
if(CLR_CMAKE_TARGET_WIN32)
add_subdirectory(gc/sample)
endif()
@@ -171,6 +173,7 @@ include_directories("classlibnative/cryptography")
include_directories("classlibnative/inc")
include_directories("${GENERATED_INCLUDE_DIR}")
include_directories("hosts/inc")
+include_directories("minipal")
if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE)
include_directories("${GENERATED_INCLUDE_DIR}/etw")
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
index c1a968ed9f994..83be00cf5e03f 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
@@ -322,33 +322,14 @@ public void DisableComObjectEagerCleanup()
[MethodImpl(MethodImplOptions.InternalCall)]
public extern bool Join(int millisecondsTimeout);
- private static int s_optimalMaxSpinWaitsPerSpinIteration;
-
- [DllImport(RuntimeHelpers.QCall)]
- private static extern int GetOptimalMaxSpinWaitsPerSpinIterationInternal();
-
///
/// Max value to be passed into for optimal delaying. This value is normalized to be
/// appropriate for the processor.
///
internal static int OptimalMaxSpinWaitsPerSpinIteration
{
- get
- {
- int optimalMaxSpinWaitsPerSpinIteration = s_optimalMaxSpinWaitsPerSpinIteration;
- return optimalMaxSpinWaitsPerSpinIteration != 0 ? optimalMaxSpinWaitsPerSpinIteration : CalculateOptimalMaxSpinWaitsPerSpinIteration();
- }
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static int CalculateOptimalMaxSpinWaitsPerSpinIteration()
- {
- // This is done lazily because the first call to the function below in the process triggers a measurement that
- // takes a nontrivial amount of time if the measurement has not already been done in the backgorund.
- // See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value.
- s_optimalMaxSpinWaitsPerSpinIteration = GetOptimalMaxSpinWaitsPerSpinIterationInternal();
- Debug.Assert(s_optimalMaxSpinWaitsPerSpinIteration > 0);
- return s_optimalMaxSpinWaitsPerSpinIteration;
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ get;
}
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd
index 731c255468e83..ab805f370aa20 100644
--- a/src/coreclr/build-runtime.cmd
+++ b/src/coreclr/build-runtime.cmd
@@ -246,10 +246,6 @@ if NOT "%__BuildType%"=="Release" (
set __PgoOptimize=0
)
-if %__PgoOptimize%==0 (
- set __RestoreOptData=0
-)
-
set "__BinDir=%__RootBinDir%\bin\coreclr\%__TargetOS%.%__BuildArch%.%__BuildType%"
set "__IntermediatesDir=%__RootBinDir%\obj\coreclr\%__TargetOS%.%__BuildArch%.%__BuildType%"
set "__LogsDir=%__RootBinDir%\log\!__BuildType!"
@@ -335,29 +331,20 @@ REM === Restore optimization profile data
REM ===
REM =========================================================================================
-set OptDataProjectFilePath=%__ProjectDir%\.nuget\optdata\optdata.csproj
-if %__RestoreOptData% EQU 1 (
- echo %__MsgPrefix%Restoring the OptimizationData Package
- set "__BinLog=\"%__LogsDir%\OptRestore_%__TargetOS%__%__BuildArch%__%__BuildType%.binlog\""
- powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__RepoRootDir%\eng\common\msbuild.ps1" /clp:nosummary %__ArcadeScriptArgs%^
- "%OptDataProjectFilePath%" /t:Restore^
- %__CommonMSBuildArgs% %__UnprocessedBuildArgs%^
- /nodereuse:false /bl:!__BinLog!
- if not !errorlevel! == 0 (
- set __exitCode=!errorlevel!
- echo %__ErrMsgPrefix%%__MsgPrefix%Error: Failed to restore the optimization data package.
- goto ExitWithCode
- )
-)
set __PgoOptDataPath=
if %__PgoOptimize% EQU 1 (
+ set OptDataProjectFilePath=%__ProjectDir%\.nuget\optdata\optdata.csproj
+ set __OptDataRestoreArg=
+ if %__RestoreOptData% EQU 1 (
+ set __OptDataRestoreArg=/restore
+ )
set PgoDataPackagePathOutputFile=%__IntermediatesDir%\optdatapath.txt
set "__BinLog=\"%__LogsDir%\PgoVersionRead_%__TargetOS%__%__BuildArch%__%__BuildType%.binlog\""
REM Parse the optdata package versions out of msbuild so that we can pass them on to CMake
powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__RepoRootDir%\eng\common\msbuild.ps1" /clp:nosummary %__ArcadeScriptArgs%^
- "%OptDataProjectFilePath%" /t:DumpPgoDataPackagePath^
- /p:PgoDataPackagePathOutputFile="!PgoDataPackagePathOutputFile!"^
+ "!OptDataProjectFilePath!" /t:DumpPgoDataPackagePath^
+ /p:PgoDataPackagePathOutputFile="!PgoDataPackagePathOutputFile!" !__OptDataRestoreArg!^
%__CommonMSBuildArgs% %__UnprocessedBuildArgs% /bl:!__BinLog!
if not !errorlevel! == 0 (
diff --git a/src/coreclr/build-runtime.sh b/src/coreclr/build-runtime.sh
index 0b39613aac369..1d9881f281b18 100755
--- a/src/coreclr/build-runtime.sh
+++ b/src/coreclr/build-runtime.sh
@@ -39,27 +39,20 @@ setup_dirs_local()
restore_optdata()
{
local OptDataProjectFilePath="$__ProjectRoot/.nuget/optdata/optdata.csproj"
- if [[ "$__SkipRestoreOptData" == 0 && "$__IsMSBuildOnNETCoreSupported" == 1 ]]; then
- echo "Restoring the OptimizationData package"
- "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs \
- $OptDataProjectFilePath /t:Restore /m \
- -bl:"$__LogsDir/OptRestore_$__ConfigTriplet.binlog" \
- $__CommonMSBuildArgs $__UnprocessedBuildArgs \
- /nodereuse:false
- local exit_code="$?"
- if [[ "$exit_code" != 0 ]]; then
- echo "${__ErrMsgPrefix}Failed to restore the optimization data package."
- exit "$exit_code"
- fi
- fi
if [[ "$__PgoOptimize" == 1 && "$__IsMSBuildOnNETCoreSupported" == 1 ]]; then
# Parse the optdata package versions out of msbuild so that we can pass them on to CMake
local PgoDataPackagePathOutputFile="${__IntermediatesDir}/optdatapath.txt"
+ local RestoreArg=""
+
+ if [[ "$__SkipRestoreOptData" == "0" ]]; then
+ RestoreArg="/restore"
+ fi
+
# Writes into ${PgoDataPackagePathOutputFile}
- "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs $OptDataProjectFilePath /t:DumpPgoDataPackagePath \
+ "$__RepoRootDir/eng/common/msbuild.sh" /clp:nosummary $__ArcadeScriptArgs $OptDataProjectFilePath $RestoreArg /t:DumpPgoDataPackagePath \
${__CommonMSBuildArgs} /p:PgoDataPackagePathOutputFile=${PgoDataPackagePathOutputFile} \
-bl:"$__LogsDir/PgoVersionRead_$__ConfigTriplet.binlog" > /dev/null 2>&1
local exit_code="$?"
diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props
index 08fca8de6dd64..184e885470f63 100644
--- a/src/coreclr/clr.featuredefines.props
+++ b/src/coreclr/clr.featuredefines.props
@@ -9,7 +9,7 @@
true
true
true
- true
+ false
true
diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake
index eeb421cac4c2f..0485ff99a99eb 100644
--- a/src/coreclr/clrdefinitions.cmake
+++ b/src/coreclr/clrdefinitions.cmake
@@ -224,10 +224,6 @@ if(CLR_CMAKE_TARGET_WIN32)
endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
endif(CLR_CMAKE_TARGET_WIN32)
-if(CLR_CMAKE_TARGET_OSX)
- add_definitions(-DFEATURE_WRITEBARRIER_COPY)
-endif(CLR_CMAKE_TARGET_OSX)
-
if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32)
add_compile_definitions($<$>>:FEATURE_EH_FUNCLETS>)
endif (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32)
diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt
index 5d766d53da185..f0093b7cb6660 100644
--- a/src/coreclr/debug/createdump/CMakeLists.txt
+++ b/src/coreclr/debug/createdump/CMakeLists.txt
@@ -86,6 +86,7 @@ endif(CLR_CMAKE_HOST_OSX)
dbgutil
# share the PAL in the dac module
mscordaccore
+ dl
)
add_dependencies(createdump mscordaccore)
diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp
index bc444815415cf..807036fd20082 100644
--- a/src/coreclr/debug/createdump/crashinfo.cpp
+++ b/src/coreclr/debug/createdump/crashinfo.cpp
@@ -6,13 +6,17 @@
// This is for the PAL_VirtualUnwindOutOfProc read memory adapter.
CrashInfo* g_crashInfo;
+static bool ModuleInfoCompare(const ModuleInfo* lhs, const ModuleInfo* rhs) { return lhs->BaseAddress() < rhs->BaseAddress(); }
+
CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal) :
m_ref(1),
m_pid(pid),
m_ppid(-1),
+ m_hdac(nullptr),
m_gatherFrames(gatherFrames),
m_crashThread(crashThread),
- m_signal(signal)
+ m_signal(signal),
+ m_moduleInfos(&ModuleInfoCompare)
{
g_crashInfo = this;
#ifdef __APPLE__
@@ -31,6 +35,20 @@ CrashInfo::~CrashInfo()
delete thread;
}
m_threads.clear();
+
+ // Clean up the modules
+ for (ModuleInfo* module : m_moduleInfos)
+ {
+ delete module;
+ }
+ m_moduleInfos.clear();
+
+ // Unload DAC module
+ if (m_hdac != nullptr)
+ {
+ FreeLibrary(m_hdac);
+ m_hdac = nullptr;
+ }
#ifdef __APPLE__
if (m_task != 0)
{
@@ -191,7 +209,6 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr;
IXCLRDataProcess* pClrDataProcess = nullptr;
- HMODULE hdac = nullptr;
HRESULT hr = S_OK;
bool result = false;
@@ -205,13 +222,13 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
dacPath.append(MAKEDLLNAME_A("mscordaccore"));
// Load and initialize the DAC
- hdac = LoadLibraryA(dacPath.c_str());
- if (hdac == nullptr)
+ m_hdac = LoadLibraryA(dacPath.c_str());
+ if (m_hdac == nullptr)
{
fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n", dacPath.c_str(), GetLastError());
goto exit;
}
- pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(m_hdac, "CLRDataCreateInstance");
if (pfnCLRDataCreateInstance == nullptr)
{
fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError());
@@ -262,10 +279,6 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
{
pClrDataProcess->Release();
}
- if (hdac != nullptr)
- {
- FreeLibrary(hdac);
- }
return result;
}
@@ -347,10 +360,13 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess)
bool
CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess)
{
+ ReleaseHolder pSos = nullptr;
+ pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos);
+
// For each native and managed thread
for (ThreadInfo* thread : m_threads)
{
- if (!thread->UnwindThread(pClrDataProcess)) {
+ if (!thread->UnwindThread(pClrDataProcess, pSos)) {
return false;
}
}
@@ -426,9 +442,9 @@ CrashInfo::GetBaseAddressFromAddress(uint64_t address)
uint64_t
CrashInfo::GetBaseAddressFromName(const char* moduleName)
{
- for (const ModuleInfo& moduleInfo : m_moduleInfos)
+ for (const ModuleInfo* moduleInfo : m_moduleInfos)
{
- std::string name = GetFileName(moduleInfo.ModuleName());
+ std::string name = GetFileName(moduleInfo->ModuleName());
#ifdef __APPLE__
// Module names are case insenstive on MacOS
if (strcasecmp(name.c_str(), moduleName) == 0)
@@ -436,7 +452,7 @@ CrashInfo::GetBaseAddressFromName(const char* moduleName)
if (name.compare(moduleName) == 0)
#endif
{
- return moduleInfo.BaseAddress();
+ return moduleInfo->BaseAddress();
}
}
return 0;
@@ -445,14 +461,14 @@ CrashInfo::GetBaseAddressFromName(const char* moduleName)
//
// Return the module info for the base address
//
-const ModuleInfo*
+ModuleInfo*
CrashInfo::GetModuleInfoFromBaseAddress(uint64_t baseAddress)
{
ModuleInfo search(baseAddress);
- const auto& found = m_moduleInfos.find(search);
+ const auto& found = m_moduleInfos.find(&search);
if (found != m_moduleInfos.end())
{
- return &*found;
+ return *found;
}
return nullptr;
}
@@ -475,11 +491,12 @@ void
CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName)
{
ModuleInfo moduleInfo(baseAddress);
- const auto& found = m_moduleInfos.find(moduleInfo);
+ const auto& found = m_moduleInfos.find(&moduleInfo);
if (found == m_moduleInfos.end())
{
uint32_t timeStamp = 0;
uint32_t imageSize = 0;
+ bool isMainModule = false;
GUID mvid;
if (isManaged)
{
@@ -511,11 +528,18 @@ CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule*
}
if (pClrDataModule != nullptr)
{
+ ULONG32 flags = 0;
+ pClrDataModule->GetFlags(&flags);
+ isMainModule = (flags & CLRDATA_MODULE_IS_MAIN_MODULE) != 0;
pClrDataModule->GetVersionId(&mvid);
}
- TRACE("MODULE: timestamp %08x size %08x %s %s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), moduleName.c_str());
+ TRACE("MODULE: timestamp %08x size %08x %s %s%s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), isMainModule ? "*" : "", moduleName.c_str());
+ }
+ ModuleInfo* moduleInfo = new ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName);
+ if (isMainModule) {
+ m_mainModule = moduleInfo;
}
- m_moduleInfos.insert(ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName));
+ m_moduleInfos.insert(moduleInfo);
}
}
@@ -737,6 +761,31 @@ CrashInfo::TraceVerbose(const char* format, ...)
}
}
+//
+// Lookup a symbol in a module. The caller needs to call "free()" on symbol returned.
+//
+const char*
+ModuleInfo::GetSymbolName(uint64_t address)
+{
+ LoadModule();
+
+ if (m_localBaseAddress != 0)
+ {
+ uint64_t localAddress = m_localBaseAddress + (address - m_baseAddress);
+ Dl_info info;
+ if (dladdr((void*)localAddress, &info) != 0)
+ {
+ if (info.dli_sname != nullptr)
+ {
+ int status = -1;
+ char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status);
+ return status == 0 ? demangled : strdup(info.dli_sname);
+ }
+ }
+ }
+ return nullptr;
+}
+
//
// Returns just the file name portion of a file path
//
diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h
index f315b98dd2877..199144e17540e 100644
--- a/src/coreclr/debug/createdump/crashinfo.h
+++ b/src/coreclr/debug/createdump/crashinfo.h
@@ -46,6 +46,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
pid_t m_pid; // pid
pid_t m_ppid; // parent pid
pid_t m_tgid; // process group
+ HMODULE m_hdac; // dac module handle when loaded
bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info
pid_t m_crashThread; // crashing thread id or 0 if none
uint32_t m_signal; // crash signal code or 0 if none
@@ -68,7 +69,12 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
std::set m_otherMappings; // other memory mappings
std::set m_memoryRegions; // memory regions from DAC, etc.
std::set m_moduleAddresses; // memory region to module base address
- std::set m_moduleInfos; // module infos (base address and module name)
+ std::set m_moduleInfos; // module infos (base address and module name)
+ ModuleInfo* m_mainModule; // the module containing "Main"
+
+ // no public copy constructor
+ CrashInfo(const CrashInfo&) = delete;
+ void operator=(const CrashInfo&) = delete;
public:
CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal);
@@ -82,7 +88,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory
uint64_t GetBaseAddressFromAddress(uint64_t address);
uint64_t GetBaseAddressFromName(const char* moduleName);
- const ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress);
+ ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress);
void AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress);
void AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName);
void InsertMemoryRegion(uint64_t address, size_t size);
@@ -98,6 +104,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
inline const pid_t CrashThread() const { return m_crashThread; }
inline const uint32_t Signal() const { return m_signal; }
inline const std::string& Name() const { return m_name; }
+ inline const ModuleInfo* MainModule() const { return m_mainModule; }
inline const std::vector Threads() const { return m_threads; }
inline const std::set ModuleMappings() const { return m_moduleMappings; }
diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp
index 4461d2a8c96a5..ad9c247e37dfd 100644
--- a/src/coreclr/debug/createdump/crashinfomac.cpp
+++ b/src/coreclr/debug/createdump/crashinfomac.cpp
@@ -380,3 +380,39 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r
*read = numberOfBytesRead;
return size == 0 || numberOfBytesRead > 0;
}
+
+const struct dyld_all_image_infos* g_image_infos = nullptr;
+
+void
+ModuleInfo::LoadModule()
+{
+ if (m_module == nullptr)
+ {
+ m_module = dlopen(m_moduleName.c_str(), RTLD_LAZY);
+ if (m_module != nullptr)
+ {
+ if (g_image_infos == nullptr)
+ {
+ struct task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ kern_return_t result = task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ if (result == KERN_SUCCESS)
+ {
+ g_image_infos = (const struct dyld_all_image_infos*)dyld_info.all_image_info_addr;
+ }
+ }
+ if (g_image_infos != nullptr)
+ {
+ for (int i = 0; i < g_image_infos->infoArrayCount; ++i)
+ {
+ const struct dyld_image_info* image = g_image_infos->infoArray + i;
+ if (strcasecmp(image->imageFilePath, m_moduleName.c_str()) == 0)
+ {
+ m_localBaseAddress = (uint64_t)image->imageLoadAddress;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp
index 2cc1eb6c688fb..2f9605554ad1f 100644
--- a/src/coreclr/debug/createdump/crashinfounix.cpp
+++ b/src/coreclr/debug/createdump/crashinfounix.cpp
@@ -414,3 +414,13 @@ GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name)
fclose(statusFile);
return true;
}
+
+void
+ModuleInfo::LoadModule()
+{
+ if (m_module == nullptr)
+ {
+ m_module = dlopen(m_moduleName.c_str(), RTLD_LAZY);
+ m_localBaseAddress = ((struct link_map*)m_module)->l_addr;
+ }
+}
diff --git a/src/coreclr/debug/createdump/crashreportwriter.cpp b/src/coreclr/debug/createdump/crashreportwriter.cpp
index 9cac899968f88..40d8dbdba767a 100644
--- a/src/coreclr/debug/createdump/crashreportwriter.cpp
+++ b/src/coreclr/debug/createdump/crashreportwriter.cpp
@@ -51,20 +51,19 @@ CrashReportWriter::WriteCrashReport(const std::string& dumpFileName)
}
}
-#ifdef __APPLE__
-
void
CrashReportWriter::WriteCrashReport()
{
- const char* exceptionType = nullptr;
OpenObject("payload");
- WriteValue("protocol_version", "0.0.7");
+ WriteValue("protocol_version", "1.0.0");
OpenObject("configuration");
#if defined(__x86_64__)
WriteValue("architecture", "amd64");
#elif defined(__aarch64__)
WriteValue("architecture", "arm64");
+#elif defined(__arm__)
+ WriteValue("architecture", "arm");
#endif
std::string version;
assert(strncmp(sccsid, "@(#)Version ", 12) == 0);
@@ -73,6 +72,13 @@ CrashReportWriter::WriteCrashReport()
WriteValue("version", version.c_str());
CloseObject(); // configuration
+ // The main module was saved away in the crash info
+ if (m_crashInfo.MainModule()->BaseAddress() != 0)
+ {
+ WriteValue("process_name", GetFileName(m_crashInfo.MainModule()->ModuleName()).c_str());
+ }
+
+ const char* exceptionType = nullptr;
OpenArray("threads");
for (const ThreadInfo* thread : m_crashInfo.Threads())
{
@@ -131,6 +137,10 @@ CrashReportWriter::WriteCrashReport()
{
WriteValue("managed_exception_type", thread->ManagedExceptionType().c_str());
}
+ if (thread->ManagedExceptionHResult() != 0)
+ {
+ WriteValue32("managed_exception_hresult", thread->ManagedExceptionHResult());
+ }
WriteValue64("native_thread_id", thread->Tid());
OpenObject("ctx");
WriteValue64("IP", thread->GetInstructionPointer());
@@ -148,7 +158,7 @@ CrashReportWriter::WriteCrashReport()
}
CloseArray(); // threads
CloseObject(); // payload
-
+#ifdef __APPLE__
OpenObject("parameters");
if (exceptionType != nullptr)
{
@@ -158,8 +168,35 @@ CrashReportWriter::WriteCrashReport()
WriteSysctl("hw.model", "SystemModel");
WriteValue("SystemManufacturer", "apple");
CloseObject(); // parameters
+#endif // __APPLE__
}
+#ifdef __APPLE__
+
+void
+CrashReportWriter::WriteSysctl(const char* sysctlname, const char* valueName)
+{
+ size_t size = 0;
+ if (sysctlbyname(sysctlname, nullptr, &size, NULL, 0) >= 0)
+ {
+ ArrayHolder buffer = new char[size];
+ if (sysctlbyname(sysctlname, buffer, &size, NULL, 0) >= 0)
+ {
+ WriteValue(valueName, buffer);
+ }
+ else
+ {
+ TRACE("sysctlbyname(%s) 1 FAILED %s\n", sysctlname, strerror(errno));
+ }
+ }
+ else
+ {
+ TRACE("sysctlbyname(%s) 2 FAILED %s\n", sysctlname, strerror(errno));
+ }
+}
+
+#endif // __APPLE__
+
void
CrashReportWriter::WriteStackFrame(const StackFrame& frame)
{
@@ -167,16 +204,26 @@ CrashReportWriter::WriteStackFrame(const StackFrame& frame)
WriteValueBool("is_managed", frame.IsManaged());
WriteValue64("module_address", frame.ModuleAddress());
WriteValue64("stack_pointer", frame.StackPointer());
- WriteValue64("native_address", frame.ReturnAddress());
+ WriteValue64("native_address", frame.InstructionPointer());
WriteValue64("native_offset", frame.NativeOffset());
if (frame.IsManaged())
{
WriteValue32("token", frame.Token());
WriteValue32("il_offset", frame.ILOffset());
}
+ IXCLRDataMethodInstance* pMethod = frame.GetMethod();
+ if (pMethod != nullptr)
+ {
+ ArrayHolder wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
+ if (SUCCEEDED(pMethod->GetName(0, MAX_LONGPATH, nullptr, wszUnicodeName)))
+ {
+ std::string methodName = FormatString("%S", wszUnicodeName.GetPtr());
+ WriteValue("method_name", methodName.c_str());
+ }
+ }
if (frame.ModuleAddress() != 0)
{
- const ModuleInfo* moduleInfo = m_crashInfo.GetModuleInfoFromBaseAddress(frame.ModuleAddress());
+ ModuleInfo* moduleInfo = m_crashInfo.GetModuleInfoFromBaseAddress(frame.ModuleAddress());
if (moduleInfo != nullptr)
{
std::string moduleName = GetFileName(moduleInfo->ModuleName());
@@ -189,6 +236,12 @@ CrashReportWriter::WriteStackFrame(const StackFrame& frame)
}
else
{
+ const char* symbol = moduleInfo->GetSymbolName(frame.InstructionPointer());
+ if (symbol != nullptr)
+ {
+ WriteValue("unmanaged_name", symbol);
+ free((void*)symbol);
+ }
WriteValue("native_module", moduleName.c_str());
}
}
@@ -196,38 +249,8 @@ CrashReportWriter::WriteStackFrame(const StackFrame& frame)
CloseObject();
}
-void
-CrashReportWriter::WriteSysctl(const char* sysctlname, const char* valueName)
-{
- size_t size = 0;
- if (sysctlbyname(sysctlname, nullptr, &size, NULL, 0) >= 0)
- {
- ArrayHolder buffer = new char[size];
- if (sysctlbyname(sysctlname, buffer, &size, NULL, 0) >= 0)
- {
- WriteValue(valueName, buffer);
- }
- else
- {
- TRACE("sysctlbyname(%s) 1 FAILED %s\n", sysctlname, strerror(errno));
- }
- }
- else
- {
- TRACE("sysctlbyname(%s) 2 FAILED %s\n", sysctlname, strerror(errno));
- }
-}
-
-#else // __APPLE__
-
-void
-CrashReportWriter::WriteCrashReport()
-{
-}
-
-#endif // __APPLE__
-
-bool CrashReportWriter::OpenWriter(const char* fileName)
+bool
+CrashReportWriter::OpenWriter(const char* fileName)
{
m_fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0664);
if (m_fd == -1)
@@ -239,13 +262,15 @@ bool CrashReportWriter::OpenWriter(const char* fileName)
return true;
}
-void CrashReportWriter::CloseWriter()
+void
+CrashReportWriter::CloseWriter()
{
assert(m_indent == JSON_INDENT_VALUE);
Write("\n}\n");
}
-void CrashReportWriter::Write(const std::string& text)
+void
+CrashReportWriter::Write(const std::string& text)
{
if (!DumpWriter::WriteData(m_fd, (void*)text.c_str(), text.length()))
{
@@ -253,19 +278,22 @@ void CrashReportWriter::Write(const std::string& text)
}
}
-void CrashReportWriter::Write(const char* buffer)
+void
+CrashReportWriter::Write(const char* buffer)
{
std::string text(buffer);
Write(text);
}
-void CrashReportWriter::Indent(std::string& text)
+void
+CrashReportWriter::Indent(std::string& text)
{
assert(m_indent >= 0);
text.append(m_indent, ' ');
}
-void CrashReportWriter::WriteSeperator(std::string& text)
+void
+CrashReportWriter::WriteSeperator(std::string& text)
{
if (m_comma)
{
@@ -275,7 +303,8 @@ void CrashReportWriter::WriteSeperator(std::string& text)
Indent(text);
}
-void CrashReportWriter::OpenValue(const char* key, char marker)
+void
+CrashReportWriter::OpenValue(const char* key, char marker)
{
std::string text;
WriteSeperator(text);
@@ -292,7 +321,8 @@ void CrashReportWriter::OpenValue(const char* key, char marker)
Write(text);
}
-void CrashReportWriter::CloseValue(char marker)
+void
+CrashReportWriter::CloseValue(char marker)
{
std::string text;
text.append(1, '\n');
@@ -304,7 +334,8 @@ void CrashReportWriter::CloseValue(char marker)
Write(text);
}
-void CrashReportWriter::WriteValue(const char* key, const char* value)
+void
+CrashReportWriter::WriteValue(const char* key, const char* value)
{
std::string text;
WriteSeperator(text);
@@ -317,41 +348,48 @@ void CrashReportWriter::WriteValue(const char* key, const char* value)
Write(text);
}
-void CrashReportWriter::WriteValueBool(const char* key, bool value)
+void
+CrashReportWriter::WriteValueBool(const char* key, bool value)
{
WriteValue(key, value ? "true" : "false");
}
-void CrashReportWriter::WriteValue32(const char* key, uint32_t value)
+void
+CrashReportWriter::WriteValue32(const char* key, uint32_t value)
{
char buffer[16];
snprintf(buffer, sizeof(buffer), "0x%x", value);
WriteValue(key, buffer);
}
-void CrashReportWriter::WriteValue64(const char* key, uint64_t value)
+void
+CrashReportWriter::WriteValue64(const char* key, uint64_t value)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value);
WriteValue(key, buffer);
}
-void CrashReportWriter::OpenObject(const char* key)
+void
+CrashReportWriter::OpenObject(const char* key)
{
OpenValue(key, '{');
}
-void CrashReportWriter::CloseObject()
+void
+CrashReportWriter::CloseObject()
{
CloseValue('}');
}
-void CrashReportWriter::OpenArray(const char* key)
+void
+CrashReportWriter::OpenArray(const char* key)
{
OpenValue(key, '[');
}
-void CrashReportWriter::CloseArray()
+void
+CrashReportWriter::CloseArray()
{
CloseValue(']');
}
diff --git a/src/coreclr/debug/createdump/crashreportwriter.h b/src/coreclr/debug/createdump/crashreportwriter.h
index ef77bfcac5592..e5f0f618d944f 100644
--- a/src/coreclr/debug/createdump/crashreportwriter.h
+++ b/src/coreclr/debug/createdump/crashreportwriter.h
@@ -13,6 +13,10 @@ class CrashReportWriter
bool m_comma;
CrashInfo& m_crashInfo;
+ // no public copy constructor
+ CrashReportWriter(const CrashReportWriter&) = delete;
+ void operator=(const CrashReportWriter&) = delete;
+
public:
CrashReportWriter(CrashInfo& crashInfo);
virtual ~CrashReportWriter();
@@ -21,9 +25,9 @@ class CrashReportWriter
private:
void WriteCrashReport();
#ifdef __APPLE__
- void WriteStackFrame(const StackFrame& frame);
void WriteSysctl(const char* sysctlname, const char* valueName);
#endif
+ void WriteStackFrame(const StackFrame& frame);
void Write(const std::string& text);
void Write(const char* buffer);
void Indent(std::string& text);
diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h
index 95f63f460e4af..f588867c7926f 100644
--- a/src/coreclr/debug/createdump/createdump.h
+++ b/src/coreclr/debug/createdump/createdump.h
@@ -71,6 +71,8 @@ typedef int T_CONTEXT;
#endif
#include
#include
+#include
+#include
#ifdef __APPLE__
#include
#else
diff --git a/src/coreclr/debug/createdump/datatarget.h b/src/coreclr/debug/createdump/datatarget.h
index 954ff5328b4e7..792947bafe217 100644
--- a/src/coreclr/debug/createdump/datatarget.h
+++ b/src/coreclr/debug/createdump/datatarget.h
@@ -9,6 +9,10 @@ class DumpDataTarget : public ICLRDataTarget
LONG m_ref; // reference count
CrashInfo& m_crashInfo;
+ // no public copy constructor
+ DumpDataTarget(const DumpDataTarget&) = delete;
+ void operator=(const DumpDataTarget&) = delete;
+
public:
DumpDataTarget(CrashInfo& crashInfo);
virtual ~DumpDataTarget();
diff --git a/src/coreclr/debug/createdump/dumpwriterelf.cpp b/src/coreclr/debug/createdump/dumpwriterelf.cpp
index 57249d83f7e6c..afd403212b247 100644
--- a/src/coreclr/debug/createdump/dumpwriterelf.cpp
+++ b/src/coreclr/debug/createdump/dumpwriterelf.cpp
@@ -32,12 +32,10 @@ DumpWriter::WriteDump()
ehdr.e_type = ET_CORE;
ehdr.e_machine = ELF_ARCH;
ehdr.e_version = EV_CURRENT;
- ehdr.e_shoff = sizeof(Ehdr);
- ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr);
+ ehdr.e_phoff = sizeof(Ehdr);
ehdr.e_ehsize = sizeof(Ehdr);
ehdr.e_phentsize = sizeof(Phdr);
- ehdr.e_shentsize = sizeof(Shdr);
// The ELF header only allows UINT16 for the number of program
// headers. In a core dump this equates to PT_NODE and PT_LOAD.
@@ -60,26 +58,33 @@ DumpWriter::WriteDump()
}
else {
ehdr.e_phnum = PH_HDR_CANARY;
+ ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr);
+ ehdr.e_shnum = 1;
+ ehdr.e_shoff = sizeof(Ehdr);
+ ehdr.e_shentsize = sizeof(Shdr);
}
if (!WriteData(&ehdr, sizeof(Ehdr))) {
return false;
}
- size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr));
+ size_t offset = sizeof(Ehdr) + (phnum * sizeof(Phdr));
size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize();
- // Add single section containing the actual count
- // of the program headers to be written.
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_info = phnum;
- // When section header offset is present but ehdr section num = 0
- // then is is expected that the sh_size indicates the size of the
- // section array or 1 in our case.
- shdr.sh_size = 1;
- if (!WriteData(&shdr, sizeof(shdr))) {
- return false;
+ if (ehdr.e_phnum == PH_HDR_CANARY)
+ {
+ // Add single section containing the actual count of the program headers to be written.
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_info = phnum;
+ shdr.sh_size = 1;
+ offset += sizeof(Shdr);
+
+ // When section header offset is present but ehdr section num = 0 then is is expected that
+ // the sh_size indicates the size of the section array or 1 in our case.
+ if (!WriteData(&shdr, sizeof(shdr))) {
+ return false;
+ }
}
// PT_NOTE header
diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h
index ac4dc10fd2f13..6da55da2f1375 100644
--- a/src/coreclr/debug/createdump/dumpwriterelf.h
+++ b/src/coreclr/debug/createdump/dumpwriterelf.h
@@ -36,6 +36,10 @@ class DumpWriter
CrashInfo& m_crashInfo;
BYTE m_tempBuffer[0x4000];
+ // no public copy constructor
+ DumpWriter(const DumpWriter&) = delete;
+ void operator=(const DumpWriter&) = delete;
+
public:
DumpWriter(CrashInfo& crashInfo);
virtual ~DumpWriter();
diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h
index 6be2aa7742b60..704ea08488214 100644
--- a/src/coreclr/debug/createdump/dumpwritermacho.h
+++ b/src/coreclr/debug/createdump/dumpwritermacho.h
@@ -30,6 +30,10 @@ class DumpWriter
std::vector m_threadLoadCommands;
BYTE m_tempBuffer[0x4000];
+ // no public copy constructor
+ DumpWriter(const DumpWriter&) = delete;
+ void operator=(const DumpWriter&) = delete;
+
public:
DumpWriter(CrashInfo& crashInfo);
virtual ~DumpWriter();
diff --git a/src/coreclr/debug/createdump/moduleinfo.h b/src/coreclr/debug/createdump/moduleinfo.h
index f07e50d592f08..2876562fba2dd 100644
--- a/src/coreclr/debug/createdump/moduleinfo.h
+++ b/src/coreclr/debug/createdump/moduleinfo.h
@@ -10,10 +10,27 @@ struct ModuleInfo
GUID m_mvid;
std::string m_moduleName;
bool m_isManaged;
+ void* m_module;
+ uint64_t m_localBaseAddress;
+
+ // no public copy constructor
+ ModuleInfo(const ModuleInfo&) = delete;
+ void operator=(const ModuleInfo&) = delete;
+
+ void LoadModule();
public:
+ ModuleInfo() :
+ m_baseAddress(0),
+ m_module(nullptr),
+ m_localBaseAddress(0)
+ {
+ }
+
ModuleInfo(uint64_t baseAddress) :
- m_baseAddress(baseAddress)
+ m_baseAddress(baseAddress),
+ m_module(nullptr),
+ m_localBaseAddress(0)
{
}
@@ -23,23 +40,19 @@ struct ModuleInfo
m_imageSize(imageSize),
m_mvid(*mvid),
m_moduleName(moduleName),
- m_isManaged(isManaged)
- {
- }
-
- // copy constructor
- ModuleInfo(const ModuleInfo& moduleInfo) :
- m_baseAddress(moduleInfo.m_baseAddress),
- m_timeStamp(moduleInfo.m_timeStamp),
- m_imageSize(moduleInfo.m_imageSize),
- m_mvid(moduleInfo.m_mvid),
- m_moduleName(moduleInfo.m_moduleName),
- m_isManaged(moduleInfo.m_isManaged)
+ m_isManaged(isManaged),
+ m_module(nullptr),
+ m_localBaseAddress(0)
{
}
~ModuleInfo()
{
+ if (m_module != nullptr)
+ {
+ dlclose(m_module);
+ m_module = nullptr;
+ }
}
inline bool IsManaged() const { return m_isManaged; }
@@ -49,13 +62,5 @@ struct ModuleInfo
inline const GUID* Mvid() const { return &m_mvid; }
inline const std::string& ModuleName() const { return m_moduleName; }
- bool operator<(const ModuleInfo& rhs) const
- {
- return m_baseAddress < rhs.m_baseAddress;
- }
-
- void Trace() const
- {
- TRACE("%" PRIA PRIx64 " %s\n", m_baseAddress, m_moduleName.c_str());
- }
+ const char* GetSymbolName(uint64_t address);
};
diff --git a/src/coreclr/debug/createdump/stackframe.h b/src/coreclr/debug/createdump/stackframe.h
index 90db2697b812b..75e20d93120c0 100644
--- a/src/coreclr/debug/createdump/stackframe.h
+++ b/src/coreclr/debug/createdump/stackframe.h
@@ -5,45 +5,75 @@ struct StackFrame
{
private:
uint64_t m_moduleAddress;
- uint64_t m_returnAddress;
+ uint64_t m_instructionPointer;
uint64_t m_stackPointer;
uint32_t m_nativeOffset;
uint32_t m_token;
uint32_t m_ilOffset;
+ IXCLRDataMethodInstance* m_pMethod;
bool m_isManaged;
public:
// Create native stack frame
- StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset) :
+ StackFrame(uint64_t moduleAddress, uint64_t instructionPointer, uint64_t stackPointer, uint32_t nativeOffset) :
m_moduleAddress(moduleAddress),
- m_returnAddress(returnAddress),
+ m_instructionPointer(instructionPointer),
m_stackPointer(stackPointer),
m_nativeOffset(nativeOffset),
m_token(0),
m_ilOffset(0),
+ m_pMethod(nullptr),
m_isManaged(false)
{
}
// Create managed stack frame
- StackFrame(uint64_t moduleAddress, uint64_t returnAddress, uint64_t stackPointer, uint32_t nativeOffset, uint64_t token, uint32_t ilOffset) :
+ StackFrame(uint64_t moduleAddress, uint64_t instructionPointer, uint64_t stackPointer, IXCLRDataMethodInstance* pMethod, uint32_t nativeOffset, uint64_t token, uint32_t ilOffset) :
m_moduleAddress(moduleAddress),
- m_returnAddress(returnAddress),
+ m_instructionPointer(instructionPointer),
m_stackPointer(stackPointer),
m_nativeOffset(nativeOffset),
m_token(token),
m_ilOffset(ilOffset),
+ m_pMethod(pMethod),
m_isManaged(true)
{
}
+ // copy constructor
+ StackFrame(const StackFrame& frame) :
+ m_moduleAddress(frame.m_moduleAddress),
+ m_instructionPointer(frame.m_instructionPointer),
+ m_stackPointer(frame.m_stackPointer),
+ m_nativeOffset(frame.m_nativeOffset),
+ m_token(frame.m_token),
+ m_ilOffset(frame.m_ilOffset),
+ m_pMethod(frame.m_pMethod),
+ m_isManaged(frame.m_isManaged)
+ {
+ if (m_pMethod != nullptr)
+ {
+ m_pMethod->AddRef();
+ }
+ }
+
+ ~StackFrame()
+ {
+ if (m_pMethod != nullptr)
+ {
+ m_pMethod->Release();
+ m_pMethod = nullptr;
+ }
+ }
+
inline uint64_t ModuleAddress() const { return m_moduleAddress; }
- inline uint64_t ReturnAddress() const { return m_returnAddress; }
+ inline uint64_t InstructionPointer() const { return m_instructionPointer; }
inline uint64_t StackPointer() const { return m_stackPointer; }
inline uint32_t NativeOffset() const { return m_nativeOffset; }
inline uint32_t Token() const { return m_token; }
inline uint32_t ILOffset() const { return m_ilOffset; }
inline bool IsManaged() const { return m_isManaged; }
+ inline IXCLRDataMethodInstance* GetMethod() const { return m_pMethod; }
bool operator<(const StackFrame& rhs) const
{
diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp
index 99284ed040247..2107c6c1bafb7 100644
--- a/src/coreclr/debug/createdump/threadinfo.cpp
+++ b/src/coreclr/debug/createdump/threadinfo.cpp
@@ -107,7 +107,7 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext)
}
bool
-ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess)
+ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos)
{
TRACE("Unwind: thread %04x\n", Tid());
@@ -152,7 +152,15 @@ ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess)
if (SUCCEEDED(pExceptionValue->GetAddress(&exceptionObject)))
{
m_exceptionObject = exceptionObject;
- TRACE("Unwind: exception object %p\n", (void*)exceptionObject);
+ if (pSos != nullptr)
+ {
+ DacpExceptionObjectData exceptionData;
+ if (SUCCEEDED(exceptionData.Request(pSos, exceptionObject)))
+ {
+ m_exceptionHResult = exceptionData.HResult;
+ }
+ }
+ TRACE("Unwind: exception object %p exception hresult %08x\n", (void*)m_exceptionObject, m_exceptionHResult);
}
ReleaseHolder pExceptionType;
if (SUCCEEDED(pExceptionValue->GetType(&pExceptionType)))
@@ -202,6 +210,7 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk)
mdMethodDef token = 0;
uint32_t nativeOffset = 0;
uint32_t ilOffset = 0;
+ ReleaseHolder pMethod;
ReleaseHolder pFrame;
if (SUCCEEDED(pStackwalk->GetFrame(&pFrame)))
@@ -212,7 +221,6 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk)
if ((simpleType & (CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE)) != 0)
{
- ReleaseHolder pMethod;
if (SUCCEEDED(pFrame->GetMethodInstance(&pMethod)))
{
ReleaseHolder pModule;
@@ -258,7 +266,7 @@ ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk)
}
// Add managed stack frame for the crash info notes
- StackFrame frame(moduleAddress, ip, sp, nativeOffset, token, ilOffset);
+ StackFrame frame(moduleAddress, ip, sp, pMethod.Detach(), nativeOffset, token, ilOffset);
AddStackFrame(frame);
}
@@ -270,7 +278,7 @@ ThreadInfo::AddStackFrame(const StackFrame& frame)
{
TRACE("Unwind: sp %p ip %p off %08x mod %p%c\n",
(void*)frame.StackPointer(),
- (void*)frame.ReturnAddress(),
+ (void*)frame.InstructionPointer(),
frame.NativeOffset(),
(void*)frame.ModuleAddress(),
frame.IsManaged() ? '*' : ' ');
diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h
index 1a690157908c3..7ce0df5f1ec18 100644
--- a/src/coreclr/debug/createdump/threadinfo.h
+++ b/src/coreclr/debug/createdump/threadinfo.h
@@ -42,7 +42,8 @@ class ThreadInfo
pid_t m_tgid; // thread group
bool m_managed; // if true, thread has managed code running
uint64_t m_exceptionObject; // exception object address
- std::string m_exceptionType; // exception type
+ std::string m_exceptionType; // exception type
+ int32_t m_exceptionHResult; // exception HRESULT
std::set m_frames; // stack frames
#ifdef __APPLE__
@@ -64,6 +65,10 @@ class ThreadInfo
#endif
#endif // __APPLE__
+ // no public copy constructor
+ ThreadInfo(const ThreadInfo&) = delete;
+ void operator=(const ThreadInfo&) = delete;
+
public:
#ifdef __APPLE__
ThreadInfo(CrashInfo& crashInfo, pid_t tid, mach_port_t port);
@@ -73,7 +78,7 @@ class ThreadInfo
#endif
~ThreadInfo();
bool Initialize();
- bool UnwindThread(IXCLRDataProcess* pClrDataProcess);
+ bool UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos);
void GetThreadStack();
void GetThreadContext(uint32_t flags, CONTEXT* context) const;
@@ -83,6 +88,7 @@ class ThreadInfo
inline bool IsManaged() const { return m_managed; }
inline uint64_t ManagedExceptionObject() const { return m_exceptionObject; }
+ inline int32_t ManagedExceptionHResult() const { return m_exceptionHResult; }
inline std::string ManagedExceptionType() const { return m_exceptionType; }
inline const std::set StackFrames() const { return m_frames; }
@@ -108,16 +114,19 @@ class ThreadInfo
#elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__)
inline const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; }
#endif
- inline const uint64_t GetStackPointer() const
- {
#if defined(__x86_64__)
- return m_gpRegisters.rsp;
+ inline const uint64_t GetInstructionPointer() const { return m_gpRegisters.rip; }
+ inline const uint64_t GetStackPointer() const { return m_gpRegisters.rsp; }
+ inline const uint64_t GetFramePointer() const { return m_gpRegisters.rbp; }
#elif defined(__aarch64__)
- return MCREG_Sp(m_gpRegisters);
+ inline const uint64_t GetInstructionPointer() const { return MCREG_Pc(m_gpRegisters); }
+ inline const uint64_t GetStackPointer() const { return MCREG_Sp(m_gpRegisters); }
+ inline const uint64_t GetFramePointer() const { return MCREG_Fp(m_gpRegisters); }
#elif defined(__arm__)
- return m_gpRegisters.ARM_sp;
+ inline const uint64_t GetInstructionPointer() const { return m_gpRegisters.ARM_pc; }
+ inline const uint64_t GetStackPointer() const { return m_gpRegisters.ARM_sp; }
+ inline const uint64_t GetFramePointer() const { return m_gpRegisters.ARM_fp; }
#endif
- }
#endif // __APPLE__
private:
diff --git a/src/coreclr/debug/createdump/threadinfomac.cpp b/src/coreclr/debug/createdump/threadinfomac.cpp
index 3ea9151a6494f..92b9339088fca 100644
--- a/src/coreclr/debug/createdump/threadinfomac.cpp
+++ b/src/coreclr/debug/createdump/threadinfomac.cpp
@@ -8,6 +8,7 @@ ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid, mach_port_t port) :
m_tid(tid),
m_managed(false),
m_exceptionObject(0),
+ m_exceptionHResult(0),
m_port(port)
{
}
diff --git a/src/coreclr/debug/createdump/threadinfounix.cpp b/src/coreclr/debug/createdump/threadinfounix.cpp
index c1e5ca1154cca..90a4c8ab9ffe5 100644
--- a/src/coreclr/debug/createdump/threadinfounix.cpp
+++ b/src/coreclr/debug/createdump/threadinfounix.cpp
@@ -26,7 +26,8 @@ ThreadInfo::ThreadInfo(CrashInfo& crashInfo, pid_t tid) :
m_crashInfo(crashInfo),
m_tid(tid),
m_managed(false),
- m_exceptionObject(0)
+ m_exceptionObject(0),
+ m_exceptionHResult(0)
{
}
diff --git a/src/coreclr/debug/daccess/task.cpp b/src/coreclr/debug/daccess/task.cpp
index b16f85d8773ef..c3143bf243b71 100644
--- a/src/coreclr/debug/daccess/task.cpp
+++ b/src/coreclr/debug/daccess/task.cpp
@@ -2503,7 +2503,16 @@ ClrDataModule::GetFlags(
{
(*flags) |= CLRDATA_MODULE_IS_MEMORY_STREAM;
}
-
+ PTR_Assembly pAssembly = m_module->GetAssembly();
+ PTR_BaseDomain pBaseDomain = pAssembly->GetDomain();
+ if (pBaseDomain->IsAppDomain())
+ {
+ AppDomain* pAppDomain = pBaseDomain->AsAppDomain();
+ if (pAssembly == pAppDomain->GetRootAssembly())
+ {
+ (*flags) |= CLRDATA_MODULE_IS_MAIN_MODULE;
+ }
+ }
status = S_OK;
}
EX_CATCH
diff --git a/src/coreclr/debug/ee/arm64/arm64walker.cpp b/src/coreclr/debug/ee/arm64/arm64walker.cpp
index ae6e8c1fc2933..6c4dee9349700 100644
--- a/src/coreclr/debug/ee/arm64/arm64walker.cpp
+++ b/src/coreclr/debug/ee/arm64/arm64walker.cpp
@@ -171,7 +171,14 @@ BYTE* NativeWalker::SetupOrSimulateInstructionForPatchSkip(T_CONTEXT * context,
{
CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, 0xd503201f); //Add Nop in buffer
- m_pSharedPatchBypassBuffer->RipTargetFixup = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ ExecutableWriterHolder ripTargetFixupWriterHolder(&m_pSharedPatchBypassBuffer->RipTargetFixup, sizeof(UINT_PTR));
+ UINT_PTR *pRipTargetFixupRW = ripTargetFixupWriterHolder.GetRW();
+#else // HOST_OSX && HOST_ARM64
+ UINT_PTR *pRipTargetFixupRW = &m_pSharedPatchBypassBuffer->RipTargetFixup;
+#endif // HOST_OSX && HOST_ARM64
+
+ *pRipTargetFixupRW = ip; //Control Flow simulation alone is done DebuggerPatchSkip::TriggerExceptionHook
LOG((LF_CORDB, LL_INFO100000, "Arm64Walker::Simulate opcode: %x is a Control Flow instr \n", opcode));
if (walk == WALK_CALL) //initialize Lr
diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp
index b17ae8f115002..f9304d16ab070 100644
--- a/src/coreclr/debug/ee/controller.cpp
+++ b/src/coreclr/debug/ee/controller.cpp
@@ -84,8 +84,13 @@ SharedPatchBypassBuffer* DebuggerControllerPatch::GetOrCreateSharedPatchBypassBu
if (m_pSharedPatchBypassBuffer == NULL)
{
void *pSharedPatchBypassBufferRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(SharedPatchBypassBuffer));
+#if defined(HOST_OSX) && defined(HOST_ARM64)
ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX, sizeof(SharedPatchBypassBuffer));
- new (sharedPatchBypassBufferWriterHolder.GetRW()) SharedPatchBypassBuffer();
+ void *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW();
+#else // HOST_OSX && HOST_ARM64
+ void *pSharedPatchBypassBufferRW = pSharedPatchBypassBufferRX;
+#endif // HOST_OSX && HOST_ARM64
+ new (pSharedPatchBypassBufferRW) SharedPatchBypassBuffer();
m_pSharedPatchBypassBuffer = (SharedPatchBypassBuffer*)pSharedPatchBypassBufferRX;
_ASSERTE(m_pSharedPatchBypassBuffer);
@@ -4351,7 +4356,15 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
//
m_pSharedPatchBypassBuffer = patch->GetOrCreateSharedPatchBypassBuffer();
- BYTE* patchBypass = m_pSharedPatchBypassBuffer->PatchBypass;
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ ExecutableWriterHolder sharedPatchBypassBufferWriterHolder((SharedPatchBypassBuffer*)m_pSharedPatchBypassBuffer, sizeof(SharedPatchBypassBuffer));
+ SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = sharedPatchBypassBufferWriterHolder.GetRW();
+#else // HOST_OSX && HOST_ARM64
+ SharedPatchBypassBuffer *pSharedPatchBypassBufferRW = m_pSharedPatchBypassBuffer;
+#endif // HOST_OSX && HOST_ARM64
+
+ BYTE* patchBypassRX = m_pSharedPatchBypassBuffer->PatchBypass;
+ BYTE* patchBypassRW = pSharedPatchBypassBufferRW->PatchBypass;
LOG((LF_CORDB, LL_INFO10000, "DPS::DPS: Patch skip for opcode 0x%.4x at address %p buffer allocated at 0x%.8x\n", patch->opcode, patch->address, m_pSharedPatchBypassBuffer));
// Copy the instruction block over to the patch skip
@@ -4367,19 +4380,19 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
// the 2nd skip executes the new jump-stamp code and not the original method prologue code. Copying
// the code every time ensures that we have the most up-to-date version of the code in the buffer.
_ASSERTE( patch->IsBound() );
- CopyInstructionBlock(patchBypass, (const BYTE *)patch->address);
+ CopyInstructionBlock(patchBypassRW, (const BYTE *)patch->address);
// Technically, we could create a patch skipper for an inactive patch, but we rely on the opcode being
// set here.
_ASSERTE( patch->IsActivated() );
- CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypass, patch->opcode);
+ CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)patchBypassRW, patch->opcode);
LOG((LF_CORDB, LL_EVERYTHING, "SetInstruction was called\n"));
//
// Look at instruction to get some attributes
//
- NativeWalker::DecodeInstructionForPatchSkip(patchBypass, &(m_instrAttrib));
+ NativeWalker::DecodeInstructionForPatchSkip(patchBypassRX, &(m_instrAttrib));
#if defined(TARGET_AMD64)
@@ -4395,33 +4408,33 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
// Populate the RIP-relative buffer with the current value if needed
//
- BYTE* bufferBypass = m_pSharedPatchBypassBuffer->BypassBuffer;
+ BYTE* bufferBypassRW = pSharedPatchBypassBufferRW->BypassBuffer;
// Overwrite the *signed* displacement.
- int dwOldDisp = *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]);
+ int dwOldDisp = *(int*)(&patchBypassRX[m_instrAttrib.m_dwOffsetToDisp]);
int dwNewDisp = offsetof(SharedPatchBypassBuffer, BypassBuffer) -
(offsetof(SharedPatchBypassBuffer, PatchBypass) + m_instrAttrib.m_cbInstr);
- *(int*)(&patchBypass[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp;
+ *(int*)(&patchBypassRW[m_instrAttrib.m_dwOffsetToDisp]) = dwNewDisp;
// This could be an LEA, which we'll just have to change into a MOV
// and copy the original address
- if (((patchBypass[0] == 0x4C) || (patchBypass[0] == 0x48)) && (patchBypass[1] == 0x8d))
+ if (((patchBypassRX[0] == 0x4C) || (patchBypassRX[0] == 0x48)) && (patchBypassRX[1] == 0x8d))
{
- patchBypass[1] = 0x8b; // MOV reg, mem
+ patchBypassRW[1] = 0x8b; // MOV reg, mem
_ASSERTE((int)sizeof(void*) <= SharedPatchBypassBuffer::cbBufferBypass);
- *(void**)bufferBypass = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp);
+ *(void**)bufferBypassRW = (void*)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp);
}
else
{
_ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass);
// Copy the data into our buffer.
- memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize);
+ memcpy(bufferBypassRW, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize);
if (m_instrAttrib.m_fIsWrite)
{
// save the actual destination address and size so when we TriggerSingleStep() we can update the value
- m_pSharedPatchBypassBuffer->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp);
- m_pSharedPatchBypassBuffer->RipTargetFixupSize = m_instrAttrib.m_cOperandSize;
+ pSharedPatchBypassBufferRW->RipTargetFixup = (UINT_PTR)(patch->address + m_instrAttrib.m_cbInstr + dwOldDisp);
+ pSharedPatchBypassBufferRW->RipTargetFixupSize = m_instrAttrib.m_cOperandSize;
}
}
}
@@ -4490,17 +4503,17 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
#else // FEATURE_EMULATE_SINGLESTEP
#ifdef TARGET_ARM64
- patchBypass = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode);
+ patchBypassRX = NativeWalker::SetupOrSimulateInstructionForPatchSkip(context, m_pSharedPatchBypassBuffer, (const BYTE *)patch->address, patch->opcode);
#endif //TARGET_ARM64
//set eip to point to buffer...
- SetIP(context, (PCODE)patchBypass);
+ SetIP(context, (PCODE)patchBypassRX);
if (context ==(T_CONTEXT*) &c)
thread->SetThreadContext(&c);
- LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypass, patch->opcode));
+ LOG((LF_CORDB, LL_INFO10000, "DPS::DPS Bypass at 0x%p for opcode %p \n", patchBypassRX, patch->opcode));
//
// Turn on single step (if the platform supports it) so we can
diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h
index 12b1106f7a4b2..6996439c31fba 100644
--- a/src/coreclr/debug/ee/controller.h
+++ b/src/coreclr/debug/ee/controller.h
@@ -266,14 +266,28 @@ class SharedPatchBypassBuffer
LONG AddRef()
{
- LONG newRefCount = InterlockedIncrement(&m_refCount);
+#if !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64)
+ ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG));
+ LONG *pRefCountRW = refCountWriterHolder.GetRW();
+#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+ LONG *pRefCountRW = &m_refCount;
+#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+
+ LONG newRefCount = InterlockedIncrement(pRefCountRW);
_ASSERTE(newRefCount > 0);
return newRefCount;
}
LONG Release()
{
- LONG newRefCount = InterlockedDecrement(&m_refCount);
+#if !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+ ExecutableWriterHolder refCountWriterHolder(&m_refCount, sizeof(LONG));
+ LONG *pRefCountRW = refCountWriterHolder.GetRW();
+#else // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+ LONG *pRefCountRW = &m_refCount;
+#endif // !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+
+ LONG newRefCount = InterlockedDecrement(pRefCountRW);
_ASSERTE(newRefCount >= 0);
if (newRefCount == 0)
diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp
index 53ee5555ace43..e4563a31757f4 100644
--- a/src/coreclr/debug/ee/debugger.cpp
+++ b/src/coreclr/debug/ee/debugger.cpp
@@ -1317,13 +1317,19 @@ DebuggerEval::DebuggerEval(CONTEXT * pContext, DebuggerIPCE_FuncEvalInfo * pEval
// Allocate the breakpoint instruction info in executable memory.
void *bpInfoSegmentRX = g_pDebugger->GetInteropSafeExecutableHeap()->Alloc(sizeof(DebuggerEvalBreakpointInfoSegment));
+
+#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX) && defined(HOST_ARM64)
ExecutableWriterHolder bpInfoSegmentWriterHolder((DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX, sizeof(DebuggerEvalBreakpointInfoSegment));
- new (bpInfoSegmentWriterHolder.GetRW()) DebuggerEvalBreakpointInfoSegment(this);
+ DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = bpInfoSegmentWriterHolder.GetRW();
+#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+ DebuggerEvalBreakpointInfoSegment *bpInfoSegmentRW = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX;
+#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX && HOST_ARM64
+ new (bpInfoSegmentRW) DebuggerEvalBreakpointInfoSegment(this);
m_bpInfoSegment = (DebuggerEvalBreakpointInfoSegment*)bpInfoSegmentRX;
// This must be non-zero so that the saved opcode is non-zero, and on IA64 we want it to be 0x16
// so that we can have a breakpoint instruction in any slot in the bundle.
- bpInfoSegmentWriterHolder.GetRW()->m_breakpointInstruction[0] = 0x16;
+ bpInfoSegmentRW->m_breakpointInstruction[0] = 0x16;
#if defined(TARGET_ARM)
USHORT *bp = (USHORT*)&m_bpInfoSegment->m_breakpointInstruction;
*bp = CORDbg_BREAK_INSTRUCTION;
@@ -16234,6 +16240,7 @@ void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger)
}
#endif // DACCESS_COMPILE
+#ifndef DACCESS_COMPILE
/* ------------------------------------------------------------------------ *
* Functions for DebuggerHeap executable memory allocations
* ------------------------------------------------------------------------ */
@@ -16378,6 +16385,7 @@ void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(De
return page->GetPointerToChunk(chunkNumber);
}
+#endif // DACCESS_COMPILE
/* ------------------------------------------------------------------------ *
* DebuggerHeap impl
@@ -16412,7 +16420,7 @@ void DebuggerHeap::Destroy()
m_hHeap = NULL;
}
#endif
-#ifndef HOST_WINDOWS
+#if !defined(HOST_WINDOWS) && !defined(DACCESS_COMPILE)
if (m_execMemAllocator != NULL)
{
delete m_execMemAllocator;
@@ -16439,6 +16447,8 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable)
}
CONTRACTL_END;
+#ifndef DACCESS_COMPILE
+
// Have knob catch if we don't want to lazy init the debugger.
_ASSERTE(!g_DbgShouldntUseDebugger);
m_fExecutable = fExecutable;
@@ -16472,7 +16482,9 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable)
return E_OUTOFMEMORY;
}
}
-#endif
+#endif
+
+#endif // !DACCESS_COMPILE
return S_OK;
}
@@ -16549,7 +16561,10 @@ void *DebuggerHeap::Alloc(DWORD size)
size += sizeof(InteropHeapCanary);
#endif
- void *ret;
+ void *ret = NULL;
+
+#ifndef DACCESS_COMPILE
+
#ifdef USE_INTEROPSAFE_HEAP
_ASSERTE(m_hHeap != NULL);
ret = ::HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size);
@@ -16585,7 +16600,7 @@ void *DebuggerHeap::Alloc(DWORD size)
InteropHeapCanary * pCanary = InteropHeapCanary::GetFromRawAddr(ret);
ret = pCanary->GetUserAddr();
#endif
-
+#endif // !DACCESS_COMPILE
return ret;
}
@@ -16638,6 +16653,8 @@ void DebuggerHeap::Free(void *pMem)
}
CONTRACTL_END;
+#ifndef DACCESS_COMPILE
+
#ifdef USE_INTEROPSAFE_CANARY
// Check for canary
@@ -16673,6 +16690,7 @@ void DebuggerHeap::Free(void *pMem)
#endif // HOST_WINDOWS
}
#endif
+#endif // !DACCESS_COMPILE
}
#ifndef DACCESS_COMPILE
diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h
index f16f8cd6d9d9d..5503de2459099 100644
--- a/src/coreclr/debug/ee/debugger.h
+++ b/src/coreclr/debug/ee/debugger.h
@@ -1054,6 +1054,8 @@ constexpr uint64_t CHUNKS_PER_DEBUGGERHEAP=(DEBUGGERHEAP_PAGESIZE / EXPECTED_CHU
constexpr uint64_t MAX_CHUNK_MASK=((1ull << CHUNKS_PER_DEBUGGERHEAP) - 1);
constexpr uint64_t BOOKKEEPING_CHUNK_MASK (1ull << (CHUNKS_PER_DEBUGGERHEAP - 1));
+#ifndef DACCESS_COMPILE
+
// Forward declaration
struct DebuggerHeapExecutableMemoryPage;
@@ -1110,8 +1112,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage
inline void SetNextPage(DebuggerHeapExecutableMemoryPage* nextPage)
{
+#if defined(HOST_OSX) && defined(HOST_ARM64)
ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
- debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.nextPage = nextPage;
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW();
+#else
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = this;
+#endif
+ pHeapPageRW->chunks[0].bookkeeping.nextPage = nextPage;
}
inline uint64_t GetPageOccupancy() const
@@ -1124,8 +1131,13 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage
// Can't unset the bookmark chunk!
ASSERT((newOccupancy & BOOKKEEPING_CHUNK_MASK) != 0);
ASSERT(newOccupancy <= MAX_CHUNK_MASK);
+#if defined(HOST_OSX) && defined(HOST_ARM64)
ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
- debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.pageOccupancy = newOccupancy;
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW();
+#else
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = this;
+#endif
+ pHeapPageRW->chunks[0].bookkeeping.pageOccupancy = newOccupancy;
}
inline void* GetPointerToChunk(int chunkNum) const
@@ -1136,14 +1148,18 @@ struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage
DebuggerHeapExecutableMemoryPage()
{
- ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
-
SetPageOccupancy(BOOKKEEPING_CHUNK_MASK); // only the first bit is set.
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ ExecutableWriterHolder debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = debuggerHeapPageWriterHolder.GetRW();
+#else
+ DebuggerHeapExecutableMemoryPage *pHeapPageRW = this;
+#endif
for (uint8_t i = 1; i < CHUNKS_PER_DEBUGGERHEAP; i++)
{
ASSERT(i != 0);
- debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.startOfPage = this;
- debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.chunkNumber = i;
+ pHeapPageRW->chunks[i].data.startOfPage = this;
+ pHeapPageRW->chunks[i].data.chunkNumber = i;
}
}
@@ -1190,6 +1206,8 @@ class DebuggerHeapExecutableMemoryAllocator
Crst m_execMemAllocMutex;
};
+#endif // DACCESS_COMPILE
+
// ------------------------------------------------------------------------ *
// DebuggerHeap class
// For interop debugging, we need a heap that:
@@ -1201,6 +1219,8 @@ class DebuggerHeapExecutableMemoryAllocator
#define USE_INTEROPSAFE_HEAP
#endif
+class DebuggerHeapExecutableMemoryAllocator;
+
class DebuggerHeap
{
public:
diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h
index d8d14b24b5425..9d363938519c7 100644
--- a/src/coreclr/debug/inc/amd64/primitives.h
+++ b/src/coreclr/debug/inc/amd64/primitives.h
@@ -12,10 +12,6 @@
#ifndef PRIMITIVES_H_
#define PRIMITIVES_H_
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
-#include "executableallocator.h"
-#endif
-
#ifndef CORDB_ADDRESS_TYPE
typedef const BYTE CORDB_ADDRESS_TYPE;
typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
@@ -191,14 +187,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address)
{
LIMITED_METHOD_CONTRACT;
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
- ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE);
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW();
-#else // !DBI_COMPILE && !DACCESS_COMPILE
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address;
-#endif // !DBI_COMPILE && !DACCESS_COMPILE
-
- *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch)
+ *((unsigned char*)address) = 0xCC; // int 3 (single byte patch)
FlushInstructionCache(GetCurrentProcess(), address, 1);
}
@@ -209,14 +198,7 @@ inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address,
// In a DAC build, this function assumes the input is an host address.
LIMITED_METHOD_DAC_CONTRACT;
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
- ExecutableWriterHolder instructionWriterHolder(address, sizeof(unsigned char));
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW();
-#else // !DBI_COMPILE && !DACCESS_COMPILE
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address;
-#endif // !DBI_COMPILE && !DACCESS_COMPILE
-
- *((unsigned char*)addressRW) =
+ *((unsigned char*)address) =
(unsigned char) instruction; // setting one byte is important
FlushInstructionCache(GetCurrentProcess(), address, 1);
diff --git a/src/coreclr/debug/inc/arm/primitives.h b/src/coreclr/debug/inc/arm/primitives.h
index c4e2d28602e56..269281eb006be 100644
--- a/src/coreclr/debug/inc/arm/primitives.h
+++ b/src/coreclr/debug/inc/arm/primitives.h
@@ -12,10 +12,6 @@
#ifndef PRIMITIVES_H_
#define PRIMITIVES_H_
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
-#include "executableallocator.h"
-#endif
-
#ifndef THUMB_CODE
#define THUMB_CODE 1
#endif
@@ -163,14 +159,7 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address,
// In a DAC build, this function assumes the input is an host address.
LIMITED_METHOD_DAC_CONTRACT;
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
- ExecutableWriterHolder instructionWriterHolder(address, sizeof(PRD_TYPE));
- CORDB_ADDRESS_TYPE* addressRW = instructionWriterHolder.GetRW();
-#else // !DBI_COMPILE && !DACCESS_COMPILE
- CORDB_ADDRESS_TYPE* addressRW = address;
-#endif // !DBI_COMPILE && !DACCESS_COMPILE
-
- CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)addressRW;
+ CORDB_ADDRESS ptraddr = (CORDB_ADDRESS)address;
_ASSERTE(ptraddr & THUMB_CODE);
ptraddr &= ~THUMB_CODE;
diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h
index 4f4c3f7bcd8f2..05c03c7b3094f 100644
--- a/src/coreclr/debug/inc/arm64/primitives.h
+++ b/src/coreclr/debug/inc/arm64/primitives.h
@@ -150,13 +150,13 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address,
// In a DAC build, this function assumes the input is an host address.
LIMITED_METHOD_DAC_CONTRACT;
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
+#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE) && defined(HOST_OSX)
ExecutableWriterHolder instructionWriterHolder((LPVOID)address, sizeof(PRD_TYPE));
ULONGLONG ptraddr = dac_cast(instructionWriterHolder.GetRW());
-#else // !DBI_COMPILE && !DACCESS_COMPILE
+#else // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX
ULONGLONG ptraddr = dac_cast(address);
-#endif // !DBI_COMPILE && !DACCESS_COMPILE
+#endif // !DBI_COMPILE && !DACCESS_COMPILE && HOST_OSX
*(PRD_TYPE *)ptraddr = instruction;
FlushInstructionCache(GetCurrentProcess(),
address,
diff --git a/src/coreclr/debug/inc/i386/primitives.h b/src/coreclr/debug/inc/i386/primitives.h
index 313b42c5a1970..2f228b3a3a9a1 100644
--- a/src/coreclr/debug/inc/i386/primitives.h
+++ b/src/coreclr/debug/inc/i386/primitives.h
@@ -12,10 +12,6 @@
#ifndef PRIMITIVES_H_
#define PRIMITIVES_H_
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
-#include "executableallocator.h"
-#endif
-
typedef const BYTE CORDB_ADDRESS_TYPE;
typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
@@ -151,14 +147,7 @@ inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address)
{
LIMITED_METHOD_CONTRACT;
-#if !defined(DBI_COMPILE) && !defined(DACCESS_COMPILE)
- ExecutableWriterHolder breakpointWriterHolder(address, CORDbg_BREAK_INSTRUCTION_SIZE);
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = breakpointWriterHolder.GetRW();
-#else // !DBI_COMPILE && !DACCESS_COMPILE
- UNALIGNED CORDB_ADDRESS_TYPE* addressRW = address;
-#endif // !DBI_COMPILE && !DACCESS_COMPILE
-
- *((unsigned char*)addressRW) = 0xCC; // int 3 (single byte patch)
+ *((unsigned char*)address) = 0xCC; // int 3 (single byte patch)
FlushInstructionCache(GetCurrentProcess(), address, 1);
}
diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt
index fae55ecdc3ea5..9b8e4b649864d 100644
--- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt
+++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt
@@ -109,6 +109,7 @@ set(CORECLR_LIBRARIES
v3binder
System.Globalization.Native-Static
interop
+ coreclrminipal
)
if(CLR_CMAKE_TARGET_WIN32)
diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp
index 268fffcd4d7ff..6cf5283476aba 100644
--- a/src/coreclr/gc/gc.cpp
+++ b/src/coreclr/gc/gc.cpp
@@ -14646,6 +14646,7 @@ BOOL gc_heap::short_on_end_of_seg (heap_segment* seg)
BOOL sufficient_p = sufficient_space_regions (end_gen0_region_space, end_space_after_gc());
#else
BOOL sufficient_p = sufficient_space_end_seg (allocated,
+ heap_segment_committed (seg),
heap_segment_reserved (seg),
end_space_after_gc());
#endif //USE_REGIONS
@@ -37875,13 +37876,18 @@ bool gc_heap::sufficient_space_regions (size_t end_space, size_t end_space_requi
return false;
}
#else //USE_REGIONS
-BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end, size_t end_space_required)
+BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* committed, uint8_t* reserved, size_t end_space_required)
{
BOOL can_fit = FALSE;
- size_t end_seg_space = (size_t)(seg_end - start);
- if (end_seg_space > end_space_required)
+ size_t committed_space = (size_t)(committed - start);
+ size_t end_seg_space = (size_t)(reserved - start);
+ if (committed_space > end_space_required)
{
- return check_against_hard_limit (end_space_required);
+ return true;
+ }
+ else if (end_seg_space > end_space_required)
+ {
+ return check_against_hard_limit (end_space_required - committed_space);
}
else
return false;
@@ -38045,7 +38051,7 @@ BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
size_t gen0_end_space = get_gen0_end_space();
BOOL can_fit = sufficient_space_regions (gen0_end_space, end_space);
#else //USE_REGIONS
- BOOL can_fit = sufficient_space_end_seg (start, heap_segment_reserved (ephemeral_heap_segment), end_space);
+ BOOL can_fit = sufficient_space_end_seg (start, heap_segment_committed (ephemeral_heap_segment), heap_segment_reserved (ephemeral_heap_segment), end_space);
#endif //USE_REGIONS
return can_fit;
}
diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h
index 09bd97d189188..d3f3526925d9b 100644
--- a/src/coreclr/gc/gcpriv.h
+++ b/src/coreclr/gc/gcpriv.h
@@ -3182,7 +3182,7 @@ class gc_heap
BOOL& should_expand);
#ifndef USE_REGIONS
PER_HEAP
- BOOL sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end,
+ BOOL sufficient_space_end_seg (uint8_t* start, uint8_t* committed, uint8_t* reserved,
size_t end_space_required);
#endif //!USE_REGIONS
diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def
index c48872a0b9424..c7266df7dbb01 100644
--- a/src/coreclr/inc/CrstTypes.def
+++ b/src/coreclr/inc/CrstTypes.def
@@ -201,6 +201,10 @@ End
Crst Exception
End
+Crst ExecutableAllocatorLock
+ AcquiredAfter LoaderHeap ArgBasedStubCache UMEntryThunkFreeListLock
+End
+
Crst ExecuteManRangeLock
End
@@ -505,6 +509,9 @@ Crst TypeEquivalenceMap
AcquiredBefore LoaderHeap
End
+Crst UMEntryThunkFreeListLock
+End
+
Crst UniqueStack
AcquiredBefore LoaderHeap
End
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index 0d2a1db98e471..e2f1a63a20fec 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -737,6 +737,10 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_DOTNET_DiagnosticPorts, W("DiagnosticPorts"),
RETAIL_CONFIG_STRING_INFO(INTERNAL_LTTngConfig, W("LTTngConfig"), "Configuration for LTTng.")
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime")
+//
+// Executable code
+//
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 0, "Enable W^X for executable memory.");
#ifdef FEATURE_GDBJIT
///
diff --git a/src/coreclr/inc/corprof.idl b/src/coreclr/inc/corprof.idl
index 8fc965a84f6b5..d1c58f96cf97c 100644
--- a/src/coreclr/inc/corprof.idl
+++ b/src/coreclr/inc/corprof.idl
@@ -667,6 +667,9 @@ typedef enum
COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x00000080,
+ // Enables the pinned object allocation monitoring.
+ COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x00000100,
+
COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED |
COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS |
COR_PRF_HIGH_BASIC_GC |
diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h
index a1bab2ecb906c..7be482c48bb55 100644
--- a/src/coreclr/inc/crsttypes.h
+++ b/src/coreclr/inc/crsttypes.h
@@ -49,92 +49,94 @@ enum CrstType
CrstEventPipe = 31,
CrstEventStore = 32,
CrstException = 33,
- CrstExecuteManRangeLock = 34,
- CrstExternalObjectContextCache = 35,
- CrstFCall = 36,
- CrstFuncPtrStubs = 37,
- CrstFusionAppCtx = 38,
- CrstGCCover = 39,
- CrstGlobalStrLiteralMap = 40,
- CrstHandleTable = 41,
- CrstHostAssemblyMap = 42,
- CrstHostAssemblyMapAdd = 43,
- CrstIbcProfile = 44,
- CrstIJWFixupData = 45,
- CrstIJWHash = 46,
- CrstILStubGen = 47,
- CrstInlineTrackingMap = 48,
- CrstInstMethodHashTable = 49,
- CrstInterop = 50,
- CrstInteropData = 51,
- CrstIsJMCMethod = 52,
- CrstISymUnmanagedReader = 53,
- CrstJit = 54,
- CrstJitGenericHandleCache = 55,
- CrstJitInlineTrackingMap = 56,
- CrstJitPatchpoint = 57,
- CrstJitPerf = 58,
- CrstJumpStubCache = 59,
- CrstLeafLock = 60,
- CrstListLock = 61,
- CrstLoaderAllocator = 62,
- CrstLoaderAllocatorReferences = 63,
- CrstLoaderHeap = 64,
- CrstManagedObjectWrapperMap = 65,
- CrstMethodDescBackpatchInfoTracker = 66,
- CrstModule = 67,
- CrstModuleFixup = 68,
- CrstModuleLookupTable = 69,
- CrstMulticoreJitHash = 70,
- CrstMulticoreJitManager = 71,
- CrstNativeImageEagerFixups = 72,
- CrstNativeImageLoad = 73,
- CrstNls = 74,
- CrstNotifyGdb = 75,
- CrstObjectList = 76,
- CrstPEImage = 77,
- CrstPendingTypeLoadEntry = 78,
- CrstPgoData = 79,
- CrstPinnedByrefValidation = 80,
- CrstProfilerGCRefDataFreeList = 81,
- CrstProfilingAPIStatus = 82,
- CrstRCWCache = 83,
- CrstRCWCleanupList = 84,
- CrstReadyToRunEntryPointToMethodDescMap = 85,
- CrstReflection = 86,
- CrstReJITGlobalRequest = 87,
- CrstRetThunkCache = 88,
- CrstSavedExceptionInfo = 89,
- CrstSaveModuleProfileData = 90,
- CrstSecurityStackwalkCache = 91,
- CrstSigConvert = 92,
- CrstSingleUseLock = 93,
- CrstSpecialStatics = 94,
- CrstStackSampler = 95,
- CrstStressLog = 96,
- CrstStubCache = 97,
- CrstStubDispatchCache = 98,
- CrstStubUnwindInfoHeapSegments = 99,
- CrstSyncBlockCache = 100,
- CrstSyncHashLock = 101,
- CrstSystemBaseDomain = 102,
- CrstSystemDomain = 103,
- CrstSystemDomainDelayedUnloadList = 104,
- CrstThreadIdDispenser = 105,
- CrstThreadpoolTimerQueue = 106,
- CrstThreadpoolWaitThreads = 107,
- CrstThreadpoolWorker = 108,
- CrstThreadStore = 109,
- CrstTieredCompilation = 110,
- CrstTypeEquivalenceMap = 111,
- CrstTypeIDMap = 112,
- CrstUMEntryThunkCache = 113,
- CrstUniqueStack = 114,
- CrstUnresolvedClassLock = 115,
- CrstUnwindInfoTableLock = 116,
- CrstVSDIndirectionCellLock = 117,
- CrstWrapperTemplate = 118,
- kNumberOfCrstTypes = 119
+ CrstExecutableAllocatorLock = 34,
+ CrstExecuteManRangeLock = 35,
+ CrstExternalObjectContextCache = 36,
+ CrstFCall = 37,
+ CrstFuncPtrStubs = 38,
+ CrstFusionAppCtx = 39,
+ CrstGCCover = 40,
+ CrstGlobalStrLiteralMap = 41,
+ CrstHandleTable = 42,
+ CrstHostAssemblyMap = 43,
+ CrstHostAssemblyMapAdd = 44,
+ CrstIbcProfile = 45,
+ CrstIJWFixupData = 46,
+ CrstIJWHash = 47,
+ CrstILStubGen = 48,
+ CrstInlineTrackingMap = 49,
+ CrstInstMethodHashTable = 50,
+ CrstInterop = 51,
+ CrstInteropData = 52,
+ CrstIsJMCMethod = 53,
+ CrstISymUnmanagedReader = 54,
+ CrstJit = 55,
+ CrstJitGenericHandleCache = 56,
+ CrstJitInlineTrackingMap = 57,
+ CrstJitPatchpoint = 58,
+ CrstJitPerf = 59,
+ CrstJumpStubCache = 60,
+ CrstLeafLock = 61,
+ CrstListLock = 62,
+ CrstLoaderAllocator = 63,
+ CrstLoaderAllocatorReferences = 64,
+ CrstLoaderHeap = 65,
+ CrstManagedObjectWrapperMap = 66,
+ CrstMethodDescBackpatchInfoTracker = 67,
+ CrstModule = 68,
+ CrstModuleFixup = 69,
+ CrstModuleLookupTable = 70,
+ CrstMulticoreJitHash = 71,
+ CrstMulticoreJitManager = 72,
+ CrstNativeImageEagerFixups = 73,
+ CrstNativeImageLoad = 74,
+ CrstNls = 75,
+ CrstNotifyGdb = 76,
+ CrstObjectList = 77,
+ CrstPEImage = 78,
+ CrstPendingTypeLoadEntry = 79,
+ CrstPgoData = 80,
+ CrstPinnedByrefValidation = 81,
+ CrstProfilerGCRefDataFreeList = 82,
+ CrstProfilingAPIStatus = 83,
+ CrstRCWCache = 84,
+ CrstRCWCleanupList = 85,
+ CrstReadyToRunEntryPointToMethodDescMap = 86,
+ CrstReflection = 87,
+ CrstReJITGlobalRequest = 88,
+ CrstRetThunkCache = 89,
+ CrstSavedExceptionInfo = 90,
+ CrstSaveModuleProfileData = 91,
+ CrstSecurityStackwalkCache = 92,
+ CrstSigConvert = 93,
+ CrstSingleUseLock = 94,
+ CrstSpecialStatics = 95,
+ CrstStackSampler = 96,
+ CrstStressLog = 97,
+ CrstStubCache = 98,
+ CrstStubDispatchCache = 99,
+ CrstStubUnwindInfoHeapSegments = 100,
+ CrstSyncBlockCache = 101,
+ CrstSyncHashLock = 102,
+ CrstSystemBaseDomain = 103,
+ CrstSystemDomain = 104,
+ CrstSystemDomainDelayedUnloadList = 105,
+ CrstThreadIdDispenser = 106,
+ CrstThreadpoolTimerQueue = 107,
+ CrstThreadpoolWaitThreads = 108,
+ CrstThreadpoolWorker = 109,
+ CrstThreadStore = 110,
+ CrstTieredCompilation = 111,
+ CrstTypeEquivalenceMap = 112,
+ CrstTypeIDMap = 113,
+ CrstUMEntryThunkCache = 114,
+ CrstUMEntryThunkFreeListLock = 115,
+ CrstUniqueStack = 116,
+ CrstUnresolvedClassLock = 117,
+ CrstUnwindInfoTableLock = 118,
+ CrstVSDIndirectionCellLock = 119,
+ CrstWrapperTemplate = 120,
+ kNumberOfCrstTypes = 121
};
#endif // __CRST_TYPES_INCLUDED
@@ -147,11 +149,11 @@ int g_rgCrstLevelMap[] =
{
10, // CrstAppDomainCache
14, // CrstAppDomainHandleTable
- 0, // CrstArgBasedStubCache
+ 3, // CrstArgBasedStubCache
0, // CrstAssemblyList
12, // CrstAssemblyLoader
- 3, // CrstAvailableClass
- 4, // CrstAvailableParamTypes
+ 4, // CrstAvailableClass
+ 5, // CrstAvailableParamTypes
7, // CrstBaseDomain
-1, // CrstCCompRC
13, // CrstClassFactInfoHash
@@ -160,7 +162,7 @@ int g_rgCrstLevelMap[] =
6, // CrstCodeFragmentHeap
9, // CrstCodeVersioning
0, // CrstCOMCallWrapper
- 4, // CrstCOMWrapperCache
+ 5, // CrstCOMWrapperCache
3, // CrstDataTest1
0, // CrstDataTest2
0, // CrstDbgTransport
@@ -179,9 +181,10 @@ int g_rgCrstLevelMap[] =
18, // CrstEventPipe
0, // CrstEventStore
0, // CrstException
+ 0, // CrstExecutableAllocatorLock
0, // CrstExecuteManRangeLock
0, // CrstExternalObjectContextCache
- 3, // CrstFCall
+ 4, // CrstFCall
7, // CrstFuncPtrStubs
10, // CrstFusionAppCtx
10, // CrstGCCover
@@ -196,25 +199,25 @@ int g_rgCrstLevelMap[] =
3, // CrstInlineTrackingMap
17, // CrstInstMethodHashTable
20, // CrstInterop
- 4, // CrstInteropData
+ 5, // CrstInteropData
0, // CrstIsJMCMethod
7, // CrstISymUnmanagedReader
11, // CrstJit
0, // CrstJitGenericHandleCache
16, // CrstJitInlineTrackingMap
- 3, // CrstJitPatchpoint
+ 4, // CrstJitPatchpoint
-1, // CrstJitPerf
6, // CrstJumpStubCache
0, // CrstLeafLock
-1, // CrstListLock
15, // CrstLoaderAllocator
16, // CrstLoaderAllocatorReferences
- 0, // CrstLoaderHeap
+ 3, // CrstLoaderHeap
3, // CrstManagedObjectWrapperMap
14, // CrstMethodDescBackpatchInfoTracker
- 4, // CrstModule
+ 5, // CrstModule
15, // CrstModuleFixup
- 3, // CrstModuleLookupTable
+ 4, // CrstModuleLookupTable
0, // CrstMulticoreJitHash
13, // CrstMulticoreJitManager
0, // CrstNativeImageEagerFixups
@@ -222,22 +225,22 @@ int g_rgCrstLevelMap[] =
0, // CrstNls
0, // CrstNotifyGdb
2, // CrstObjectList
- 4, // CrstPEImage
+ 5, // CrstPEImage
19, // CrstPendingTypeLoadEntry
- 3, // CrstPgoData
+ 4, // CrstPgoData
0, // CrstPinnedByrefValidation
0, // CrstProfilerGCRefDataFreeList
0, // CrstProfilingAPIStatus
- 3, // CrstRCWCache
+ 4, // CrstRCWCache
0, // CrstRCWCleanupList
10, // CrstReadyToRunEntryPointToMethodDescMap
8, // CrstReflection
17, // CrstReJITGlobalRequest
- 3, // CrstRetThunkCache
+ 4, // CrstRetThunkCache
3, // CrstSavedExceptionInfo
0, // CrstSaveModuleProfileData
0, // CrstSecurityStackwalkCache
- 3, // CrstSigConvert
+ 4, // CrstSigConvert
5, // CrstSingleUseLock
0, // CrstSpecialStatics
0, // CrstStackSampler
@@ -247,7 +250,7 @@ int g_rgCrstLevelMap[] =
4, // CrstStubUnwindInfoHeapSegments
3, // CrstSyncBlockCache
0, // CrstSyncHashLock
- 4, // CrstSystemBaseDomain
+ 5, // CrstSystemBaseDomain
13, // CrstSystemDomain
0, // CrstSystemDomainDelayedUnloadList
0, // CrstThreadIdDispenser
@@ -256,13 +259,14 @@ int g_rgCrstLevelMap[] =
13, // CrstThreadpoolWorker
12, // CrstThreadStore
8, // CrstTieredCompilation
- 3, // CrstTypeEquivalenceMap
+ 4, // CrstTypeEquivalenceMap
10, // CrstTypeIDMap
- 3, // CrstUMEntryThunkCache
- 3, // CrstUniqueStack
+ 4, // CrstUMEntryThunkCache
+ 3, // CrstUMEntryThunkFreeListLock
+ 4, // CrstUniqueStack
7, // CrstUnresolvedClassLock
3, // CrstUnwindInfoTableLock
- 3, // CrstVSDIndirectionCellLock
+ 4, // CrstVSDIndirectionCellLock
3, // CrstWrapperTemplate
};
@@ -303,6 +307,7 @@ LPCSTR g_rgCrstNameMap[] =
"CrstEventPipe",
"CrstEventStore",
"CrstException",
+ "CrstExecutableAllocatorLock",
"CrstExecuteManRangeLock",
"CrstExternalObjectContextCache",
"CrstFCall",
@@ -383,6 +388,7 @@ LPCSTR g_rgCrstNameMap[] =
"CrstTypeEquivalenceMap",
"CrstTypeIDMap",
"CrstUMEntryThunkCache",
+ "CrstUMEntryThunkFreeListLock",
"CrstUniqueStack",
"CrstUnresolvedClassLock",
"CrstUnwindInfoTableLock",
diff --git a/src/coreclr/inc/executableallocator.h b/src/coreclr/inc/executableallocator.h
index ce0c6c22f890e..101178f9a4ef0 100644
--- a/src/coreclr/inc/executableallocator.h
+++ b/src/coreclr/inc/executableallocator.h
@@ -11,6 +11,191 @@
#include "utilcode.h"
#include "ex.h"
+#include "minipal.h"
+
+#ifndef DACCESS_COMPILE
+
+// This class is responsible for allocation of all the executable memory in the runtime.
+class ExecutableAllocator
+{
+ // RX address range block descriptor
+ struct BlockRX
+ {
+ // Next block in a linked list
+ BlockRX* next;
+ // Base address of the block
+ void* baseRX;
+ // Size of the block
+ size_t size;
+ // Offset of the block in the shared memory
+ size_t offset;
+ };
+
+ // RW address range block descriptor
+ struct BlockRW
+ {
+ // Next block in a linked list
+ BlockRW* next;
+ // Base address of the RW mapping of the block
+ void* baseRW;
+ // Base address of the RX mapping of the block
+ void* baseRX;
+ // Size of the block
+ size_t size;
+ // Usage reference count of the RW block. RW blocks can be reused
+ // when multiple mappings overlap in the VA space at the same time
+ // (even from multiple threads)
+ size_t refCount;
+ };
+
+ typedef void (*FatalErrorHandler)(UINT errorCode, LPCWSTR pszMessage);
+
+ // Instance of the allocator
+ static ExecutableAllocator* g_instance;
+
+ // Callback to the runtime to report fatal errors
+ static FatalErrorHandler g_fatalErrorHandler;
+
+#if USE_UPPER_ADDRESS
+ // Preferred region to allocate the code in.
+ static BYTE* g_codeMinAddr;
+ static BYTE* g_codeMaxAddr;
+ static BYTE* g_codeAllocStart;
+ // Next address to try to allocate for code in the preferred region.
+ static BYTE* g_codeAllocHint;
+#endif // USE_UPPER_ADDRESS
+
+ // Caches the COMPlus_EnableWXORX setting
+ static bool g_isWXorXEnabled;
+
+ // Head of the linked list of all RX blocks that were allocated by this allocator
+ BlockRX* m_pFirstBlockRX = NULL;
+
+ // Head of the linked list of free RX blocks that were allocated by this allocator and then backed out
+ BlockRX* m_pFirstFreeBlockRX = NULL;
+
+ // Head of the linked list of currently mapped RW blocks
+ BlockRW* m_pFirstBlockRW = NULL;
+
+ // Handle of the double mapped memory mapper
+ void *m_doubleMemoryMapperHandle = NULL;
+
+ // Maximum size of executable memory this allocator can allocate
+ size_t m_maxExecutableCodeSize;
+
+ // First free offset in the underlying shared memory. It is not used
+ // for platforms that don't use shared memory.
+ size_t m_freeOffset = 0;
+
+ // Last RW mapping cached so that it can be reused for the next mapping
+ // request if it goes into the same range.
+ BlockRW* m_cachedMapping = NULL;
+
+ // Synchronization of the public allocator methods
+ CRITSEC_COOKIE m_CriticalSection;
+
+ // Update currently cached mapping. If the passed in block is the same as the one
+ // in the cache, it keeps it cached. Otherwise it destroys the currently cached one
+ // and replaces it by the passed in one.
+ void UpdateCachedMapping(BlockRW *pBlock);
+
+ // Find existing RW block that maps the whole specified range of RX memory.
+ // Return NULL if no such block exists.
+ void* FindRWBlock(void* baseRX, size_t size);
+
+ // Add RW block to the list of existing RW blocks
+ bool AddRWBlock(void* baseRW, void* baseRX, size_t size);
+
+ // Remove RW block from the list of existing RW blocks and return the base
+ // address and size the underlying memory was mapped at.
+ // Return false if no existing RW block contains the passed in address.
+ bool RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize);
+
+ // Find a free block with the closest size >= the requested size.
+ // Returns NULL if no such block exists.
+ BlockRX* FindBestFreeBlock(size_t size);
+
+ // Return memory mapping granularity.
+ static size_t Granularity();
+
+ // Allocate a block of executable memory of the specified size.
+ // It doesn't acquire the actual virtual memory, just the
+ // range of the underlying shared memory.
+ BlockRX* AllocateBlock(size_t size, bool* pIsFreeBlock);
+
+ // Backout the block allocated by AllocateBlock in case of an
+ // error.
+ void BackoutBlock(BlockRX* pBlock, bool isFreeBlock);
+
+ // Allocate range of offsets in the underlying shared memory
+ bool AllocateOffset(size_t* pOffset, size_t size);
+
+ // Add RX block to the linked list of existing blocks
+ void AddRXBlock(BlockRX *pBlock);
+
+ // Return true if double mapping is enabled.
+ static bool IsDoubleMappingEnabled();
+
+ // Initialize the allocator instance
+ bool Initialize();
+
+public:
+
+ // Return the ExecuteAllocator singleton instance
+ static ExecutableAllocator* Instance();
+
+ // Initialize the static members of the Executable allocator and allocate
+ // and initialize the instance of it.
+ static HRESULT StaticInitialize(FatalErrorHandler fatalErrorHandler);
+
+ // Destroy the allocator
+ ~ExecutableAllocator();
+
+ // Return true if W^X is enabled
+ static bool IsWXORXEnabled();
+
+ // Use this function to initialize the g_codeAllocHint
+ // during startup. base is runtime .dll base address,
+ // size is runtime .dll virtual size.
+ static void InitCodeAllocHint(size_t base, size_t size, int randomPageOffset);
+
+ // Use this function to reset the g_codeAllocHint
+ // after unloading an AppDomain
+ static void ResetCodeAllocHint();
+
+ // Returns TRUE if p is located in near clr.dll that allows us
+ // to use rel32 IP-relative addressing modes.
+ static bool IsPreferredExecutableRange(void* p);
+
+ // Reserve the specified amount of virtual address space for executable mapping.
+ void* Reserve(size_t size);
+
+ // Reserve the specified amount of virtual address space for executable mapping.
+ // The reserved range must be within the loAddress and hiAddress. If it is not
+ // possible to reserve memory in such range, the method returns NULL.
+ void* ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress);
+
+ // Reserve the specified amount of virtual address space for executable mapping
+ // exactly at the given address.
+ void* ReserveAt(void* baseAddressRX, size_t size);
+
+ // Commit the specified range of memory. The memory can be committed as executable (RX)
+ // or non-executable (RW) based on the passed in isExecutable flag. The non-executable
+ // allocations are used to allocate data structures that need to be close to the
+ // executable code due to memory addressing performance related reasons.
+ void* Commit(void* pStart, size_t size, bool isExecutable);
+
+ // Release the executable memory block starting at the passed in address that was allocated
+ // by one of the ReserveXXX methods.
+ void Release(void* pRX);
+
+ // Map the specified block of executable memory as RW
+ void* MapRW(void* pRX, size_t size);
+
+ // Unmap the RW mapping at the specified address
+ void UnmapRW(void* pRW);
+};
+
// Holder class to map read-execute memory as read-write so that it can be modified without using read-write-execute mapping.
// At the moment the implementation is dummy, returning the same addresses for both cases and expecting them to be read-write-execute.
// The class uses the move semantics to ensure proper unmapping in case of re-assigning of the holder value.
@@ -30,13 +215,17 @@ class ExecutableWriterHolder
void Unmap()
{
+#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE)
if (m_addressRX != NULL)
{
- // TODO: mapping / unmapping for targets using double memory mapping will be added with the double mapped allocator addition
-#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE)
PAL_JitWriteProtect(false);
-#endif
}
+#else
+ if (m_addressRX != m_addressRW)
+ {
+ ExecutableAllocator::Instance()->UnmapRW((void*)m_addressRW);
+ }
+#endif
}
public:
@@ -62,9 +251,11 @@ class ExecutableWriterHolder
ExecutableWriterHolder(T* addressRX, size_t size)
{
m_addressRX = addressRX;
+#if defined(HOST_OSX) && defined(HOST_ARM64)
m_addressRW = addressRX;
-#if defined(HOST_OSX) && defined(HOST_ARM64) && !defined(DACCESS_COMPILE)
PAL_JitWriteProtect(true);
+#else
+ m_addressRW = (T *)ExecutableAllocator::Instance()->MapRW((void*)addressRX, size);
#endif
}
@@ -79,3 +270,5 @@ class ExecutableWriterHolder
return m_addressRW;
}
};
+
+#endif // !DACCESS_COMPILE
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index fb65ea9fa613c..3c42f0850850b 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -302,12 +302,12 @@
#endif // !FEATURE_EH_FUNCLETS
#ifdef TARGET_X86
- JITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB)
- JITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EAX, JIT_WriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBX, JIT_WriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ECX, JIT_WriteBarrierECX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_ESI, JIT_WriteBarrierESI, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EDI, JIT_WriteBarrierEDI, CORINFO_HELP_SIG_NO_ALIGN_STUB)
+ DYNAMICJITHELPER(CORINFO_HELP_ASSIGN_REF_EBP, JIT_WriteBarrierEBP, CORINFO_HELP_SIG_NO_ALIGN_STUB)
JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EAX, JIT_CheckedWriteBarrierEAX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
JITHELPER(CORINFO_HELP_CHECKED_ASSIGN_REF_EBX, JIT_CheckedWriteBarrierEBX, CORINFO_HELP_SIG_NO_ALIGN_STUB)
diff --git a/src/coreclr/inc/profilepriv.inl b/src/coreclr/inc/profilepriv.inl
index 08ba58f5623f1..e99591c5ffd18 100644
--- a/src/coreclr/inc/profilepriv.inl
+++ b/src/coreclr/inc/profilepriv.inl
@@ -1860,6 +1860,21 @@ inline BOOL CORProfilerTrackLargeAllocations()
(&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED));
}
+inline BOOL CORProfilerTrackPinnedAllocations()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ return
+ (CORProfilerPresent() &&
+ (&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED));
+}
+
inline BOOL CORProfilerEnableRejit()
{
CONTRACTL
diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h
index a47034ee2e05c..77df9dfa94d2a 100644
--- a/src/coreclr/inc/utilcode.h
+++ b/src/coreclr/inc/utilcode.h
@@ -1014,35 +1014,6 @@ void SplitPath(__in SString const &path,
#define CLRGetTickCount64() GetTickCount64()
-//
-// Use this function to initialize the s_CodeAllocHint
-// during startup. base is runtime .dll base address,
-// size is runtime .dll virtual size.
-//
-void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset);
-
-
-//
-// Use this function to reset the s_CodeAllocHint
-// after unloading an AppDomain
-//
-void ResetCodeAllocHint();
-
-//
-// Returns TRUE if p is located in near clr.dll that allows us
-// to use rel32 IP-relative addressing modes.
-//
-BOOL IsPreferredExecutableRange(void * p);
-
-//
-// Allocate free memory that will be used for executable code
-// Handles the special requirements that we have on 64-bit platforms
-// where we want the executable memory to be located near mscorwks
-//
-BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
- DWORD flAllocationType,
- DWORD flProtect);
-
//
// Allocate free memory within the range [pMinAddr..pMaxAddr] using
// ClrVirtualQuery to find free memory and ClrVirtualAlloc to allocate it.
diff --git a/src/coreclr/inc/xclrdata.idl b/src/coreclr/inc/xclrdata.idl
index 818915a29ccec..aeddf9529bee4 100644
--- a/src/coreclr/inc/xclrdata.idl
+++ b/src/coreclr/inc/xclrdata.idl
@@ -1014,6 +1014,7 @@ typedef enum
CLRDATA_MODULE_DEFAULT = 0x00000000,
CLRDATA_MODULE_IS_DYNAMIC = 0x00000001,
CLRDATA_MODULE_IS_MEMORY_STREAM = 0x00000002,
+ CLRDATA_MODULE_IS_MAIN_MODULE = 0x00000004,
} CLRDataModuleFlag;
typedef enum
diff --git a/src/coreclr/inc/yieldprocessornormalized.h b/src/coreclr/inc/yieldprocessornormalized.h
index ba349bb83ad56..121e60b033356 100644
--- a/src/coreclr/inc/yieldprocessornormalized.h
+++ b/src/coreclr/inc/yieldprocessornormalized.h
@@ -12,14 +12,59 @@ FORCEINLINE void System_YieldProcessor() { YieldProcessor(); }
#endif
#define YieldProcessor Dont_Use_YieldProcessor
-const unsigned int MinNsPerNormalizedYield = 37; // measured typically 37-46 on post-Skylake
-const unsigned int NsPerOptimalMaxSpinIterationDuration = 272; // approx. 900 cycles, measured 281 on pre-Skylake, 263 on post-Skylake
+#define DISABLE_COPY(T) \
+ T(const T &) = delete; \
+ T &operator =(const T &) = delete
-extern unsigned int g_yieldsPerNormalizedYield;
-extern unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration;
+#define DISABLE_CONSTRUCT_COPY(T) \
+ T() = delete; \
+ DISABLE_COPY(T)
-void InitializeYieldProcessorNormalizedCrst();
-void EnsureYieldProcessorNormalizedInitialized();
+class YieldProcessorNormalization
+{
+public:
+ static const unsigned int TargetNsPerNormalizedYield = 37;
+ static const unsigned int TargetMaxNsPerSpinIteration = 272;
+
+ // These are maximums for the computed values for normalization based their calculation
+ static const unsigned int MaxYieldsPerNormalizedYield = TargetNsPerNormalizedYield * 10;
+ static const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration =
+ TargetMaxNsPerSpinIteration * 3 / (TargetNsPerNormalizedYield * 2) + 1;
+
+private:
+ static bool s_isMeasurementScheduled;
+
+ static unsigned int s_yieldsPerNormalizedYield;
+ static unsigned int s_optimalMaxNormalizedYieldsPerSpinIteration;
+
+public:
+ static bool IsMeasurementScheduled()
+ {
+ return s_isMeasurementScheduled;
+ }
+
+ static void PerformMeasurement();
+
+private:
+ static void ScheduleMeasurementIfNecessary();
+
+public:
+ static unsigned int GetOptimalMaxNormalizedYieldsPerSpinIteration()
+ {
+ return s_optimalMaxNormalizedYieldsPerSpinIteration;
+ }
+
+ static void FireMeasurementEvents();
+
+private:
+ static double AtomicLoad(double *valueRef);
+ static void AtomicStore(double *valueRef, double value);
+
+ DISABLE_CONSTRUCT_COPY(YieldProcessorNormalization);
+
+ friend class YieldProcessorNormalizationInfo;
+ friend void YieldProcessorNormalizedForPreSkylakeCount(unsigned int);
+};
class YieldProcessorNormalizationInfo
{
@@ -30,12 +75,15 @@ class YieldProcessorNormalizationInfo
public:
YieldProcessorNormalizationInfo()
- : yieldsPerNormalizedYield(g_yieldsPerNormalizedYield),
- optimalMaxNormalizedYieldsPerSpinIteration(g_optimalMaxNormalizedYieldsPerSpinIteration),
+ : yieldsPerNormalizedYield(YieldProcessorNormalization::s_yieldsPerNormalizedYield),
+ optimalMaxNormalizedYieldsPerSpinIteration(YieldProcessorNormalization::s_optimalMaxNormalizedYieldsPerSpinIteration),
optimalMaxYieldsPerSpinIteration(yieldsPerNormalizedYield * optimalMaxNormalizedYieldsPerSpinIteration)
{
+ YieldProcessorNormalization::ScheduleMeasurementIfNecessary();
}
+ DISABLE_COPY(YieldProcessorNormalizationInfo);
+
friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &);
friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &, unsigned int);
friend void YieldProcessorNormalizedForPreSkylakeCount(const YieldProcessorNormalizationInfo &, unsigned int);
@@ -98,9 +146,8 @@ FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo
if (sizeof(SIZE_T) <= sizeof(unsigned int))
{
- // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield
- // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized().
- const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield;
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
if (count > MaxCount)
{
count = MaxCount;
@@ -144,9 +191,8 @@ FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(
if (sizeof(SIZE_T) <= sizeof(unsigned int))
{
- // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield
- // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized().
- const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield;
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
if (preSkylakeCount > MaxCount)
{
preSkylakeCount = MaxCount;
@@ -175,7 +221,35 @@ FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(
// }
FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(unsigned int preSkylakeCount)
{
- YieldProcessorNormalizedForPreSkylakeCount(YieldProcessorNormalizationInfo(), preSkylakeCount);
+ // This function does not forward to the one above because it is used by some code under utilcode, where
+ // YieldProcessorNormalizationInfo cannot be used since normalization does not happen in some of its consumers. So this
+ // version uses the fields in YieldProcessorNormalization directly.
+
+ _ASSERTE(preSkylakeCount != 0);
+
+ if (sizeof(SIZE_T) <= sizeof(unsigned int))
+ {
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
+ if (preSkylakeCount > MaxCount)
+ {
+ preSkylakeCount = MaxCount;
+ }
+ }
+
+ const unsigned int PreSkylakeCountToSkylakeCountDivisor = 8;
+ SIZE_T n =
+ (SIZE_T)preSkylakeCount *
+ YieldProcessorNormalization::s_yieldsPerNormalizedYield /
+ PreSkylakeCountToSkylakeCountDivisor;
+ if (n == 0)
+ {
+ n = 1;
+ }
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
}
// See YieldProcessorNormalized() for preliminary info. This function is to be used when there is a decent possibility that the
@@ -193,15 +267,12 @@ FORCEINLINE void YieldProcessorWithBackOffNormalized(
const YieldProcessorNormalizationInfo &normalizationInfo,
unsigned int spinIteration)
{
- // normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration cannot exceed the value below based on calculations done in
- // InitializeYieldProcessorNormalized()
- const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration =
- NsPerOptimalMaxSpinIterationDuration * 3 / (MinNsPerNormalizedYield * 2) + 1;
- _ASSERTE(normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration);
-
- // This shift value should be adjusted based on the asserted condition below
+ // This shift value should be adjusted based on the asserted conditions below
const UINT8 MaxShift = 3;
- static_assert_no_msg(((unsigned int)1 << (MaxShift + 1)) >= MaxOptimalMaxNormalizedYieldsPerSpinIteration);
+ static_assert_no_msg(
+ ((unsigned int)1 << MaxShift) <= YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration);
+ static_assert_no_msg(
+ ((unsigned int)1 << (MaxShift + 1)) > YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration);
unsigned int n;
if (spinIteration <= MaxShift &&
@@ -219,3 +290,6 @@ FORCEINLINE void YieldProcessorWithBackOffNormalized(
System_YieldProcessor();
} while (--n != 0);
}
+
+#undef DISABLE_CONSTRUCT_COPY
+#undef DISABLE_COPY
diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp
index 128c4e033d9fa..5df8c1e4358c2 100644
--- a/src/coreclr/jit/block.cpp
+++ b/src/coreclr/jit/block.cpp
@@ -1431,6 +1431,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
/* Give the block a number, set the ancestor count and weight */
++fgBBcount;
+ ++fgBBNumMax;
if (compIsForInlining())
{
@@ -1438,7 +1439,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
}
else
{
- block->bbNum = ++fgBBNumMax;
+ block->bbNum = fgBBNumMax;
}
if (compRationalIRForm)
diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index 0cbe51281b353..5d42a1626536f 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -29,7 +29,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "jithashtable.h"
/*****************************************************************************/
-typedef BitVec EXPSET_TP;
+typedef BitVec EXPSET_TP;
+typedef BitVec_ValArg_T EXPSET_VALARG_TP;
+typedef BitVec_ValRet_T EXPSET_VALRET_TP;
+
#if LARGE_EXPSET
#define EXPSET_SZ 64
#else
diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis
index 90a9ff703a471..b3c187474900f 100644
--- a/src/coreclr/jit/clrjit.natvis
+++ b/src/coreclr/jit/clrjit.natvis
@@ -5,6 +5,13 @@ Licensed to the .NET Foundation under one or more agreements.
The .NET Foundation licenses this file to you under the MIT license.
-->
+
@@ -183,4 +190,48 @@ The .NET Foundation licenses this file to you under the MIT license.
+
+ size={m_nSize,d} capacity={m_nCapacity,d}
+ Empty
+
+
+ m_nSize
+ m_pArray
+
+
+
+
+
+ size={m_size,d}
+ Empty
+
+
+ m_size
+ m_members
+
+
+
+
+
+ size={m_size,d} used={m_used,d}
+ Empty
+
+
+ m_used
+ m_members
+
+
+
+
+
+
+
+
+ {optType,en}
+
+ (LcJaggedArrayOptInfo*)this,nd
+ (LcMdArrayOptInfo*)this,nd
+
+
+
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 132a763c06b01..626cb3e5b7bd6 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -216,6 +216,7 @@ class CodeGen final : public CodeGenInterface
unsigned genCurDispOffset;
static const char* genInsName(instruction ins);
+ const char* genInsDisplayName(emitter::instrDesc* id);
#endif // DEBUG
//-------------------------------------------------------------------------
@@ -1503,10 +1504,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void instGen_Store_Reg_Into_Lcl(var_types dstType, regNumber srcReg, int varNum, int offs);
-#ifdef DEBUG
- void __cdecl instDisp(instruction ins, bool noNL, const char* fmt, ...);
-#endif
-
#ifdef TARGET_XARCH
instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue);
#endif // TARGET_XARCH
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index aeecb0553b989..d2e08159ec104 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -1070,7 +1070,7 @@ void CodeGen::genDefineTempLabel(BasicBlock* label)
{
genLogLabel(label);
label->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label->bbNum));
+ gcInfo.gcRegByrefSetCur, false DEBUG_ARG(label));
}
// genDefineInlineTempLabel: Define an inline label that does not affect the GC
@@ -2064,9 +2064,8 @@ void CodeGen::genInsertNopForUnwinder(BasicBlock* block)
// block starts an EH region. If we pointed the existing bbEmitCookie here, then the NOP
// would be executed, which we would prefer not to do.
- block->bbUnwindNopEmitCookie =
- GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
- false DEBUG_ARG(block->bbNum));
+ block->bbUnwindNopEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
+ gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block));
instGen(INS_nop);
}
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index b822f233e66db..aa2fb0f58955f 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -356,7 +356,7 @@ void CodeGen::genCodeForBBlist()
// Mark a label and update the current set of live GC refs
block->bbEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block->bbNum));
+ gcInfo.gcRegByrefSetCur, false DEBUG_ARG(block));
}
if (block == compiler->fgFirstColdBlock)
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 7019d8afad6c5..fe5734213afcc 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -2988,6 +2988,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
opts.disAsmSpilled = false;
opts.disDiffable = false;
opts.disAddr = false;
+ opts.disAlignment = false;
opts.dspCode = false;
opts.dspEHTable = false;
opts.dspDebugInfo = false;
@@ -3136,6 +3137,11 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
opts.disAddr = true;
}
+ if (JitConfig.JitDasmWithAlignmentBoundaries() != 0)
+ {
+ opts.disAlignment = true;
+ }
+
if (JitConfig.JitLongAddress() != 0)
{
opts.compLongAddress = true;
@@ -6266,6 +6272,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
// a potential inline candidate.
InlineResult prejitResult(this, methodHnd, "prejit");
+ // Profile data allows us to avoid early "too many IL bytes" outs.
+ prejitResult.NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, fgHaveSufficientProfileData());
+
// Do the initial inline screen.
impCanInlineIL(methodHnd, methodInfo, forceInline, &prejitResult);
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 1383b57a7dd3a..9027a5d233529 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -5814,6 +5814,7 @@ class Compiler
void WalkSpanningTree(SpanningTreeVisitor* visitor);
void fgSetProfileWeight(BasicBlock* block, BasicBlock::weight_t weight);
void fgApplyProfileScale();
+ bool fgHaveSufficientProfileData();
// fgIsUsingProfileWeights - returns true if we have real profile data for this method
// or if we have some fake profile data for the stress mode
@@ -6237,9 +6238,9 @@ class Compiler
public:
void optInit();
- GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt);
- GenTree* Compiler::optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt);
- void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt);
+ GenTree* optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt);
+ GenTree* optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt);
+ void optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt);
bool optIsRangeCheckRemovable(GenTree* tree);
protected:
@@ -6728,7 +6729,7 @@ class Compiler
// BitVec trait information for computing CSE availability using the CSE_DataFlow algorithm.
// Two bits are allocated per CSE candidate to compute CSE availability
// plus an extra bit to handle the initial unvisited case.
- // (See CSE_DataFlow::EndMerge for an explaination of why this is necessary)
+ // (See CSE_DataFlow::EndMerge for an explanation of why this is necessary.)
//
// The two bits per CSE candidate have the following meanings:
// 11 - The CSE is available, and is also available when considering calls as killing availability.
@@ -6738,6 +6739,37 @@ class Compiler
//
BitVecTraits* cseLivenessTraits;
+ //-----------------------------------------------------------------------------------------------------------------
+ // getCSEnum2bit: Return the normalized index to use in the EXPSET_TP for the CSE with the given CSE index.
+ // Each GenTree has a `gtCSEnum` field. Zero is reserved to mean this node is not a CSE, positive values indicate
+ // CSE uses, and negative values indicate CSE defs. The caller must pass a non-zero positive value, as from
+ // GET_CSE_INDEX().
+ //
+ static unsigned genCSEnum2bit(unsigned CSEnum)
+ {
+ assert((CSEnum > 0) && (CSEnum <= MAX_CSE_CNT));
+ return CSEnum - 1;
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // getCSEAvailBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit for a CSE.
+ //
+ static unsigned getCSEAvailBit(unsigned CSEnum)
+ {
+ return genCSEnum2bit(CSEnum) * 2;
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // getCSEAvailCrossCallBit: Return the bit used by CSE dataflow sets (bbCseGen, etc.) for the availability bit
+ // for a CSE considering calls as killing availability bit (see description above).
+ //
+ static unsigned getCSEAvailCrossCallBit(unsigned CSEnum)
+ {
+ return getCSEAvailBit(CSEnum) + 1;
+ }
+
+ void optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits = true);
+
EXPSET_TP cseCallKillsMask; // Computed once - A mask that is used to kill available CSEs at callsites
/* Generic list of nodes - used by the CSE logic */
@@ -6872,9 +6904,12 @@ class Compiler
return (enckey & ~TARGET_SIGN_BIT) << CSE_CONST_SHARED_LOW_BITS;
}
- /**************************************************************************
- * Value Number based CSEs
- *************************************************************************/
+/**************************************************************************
+ * Value Number based CSEs
+ *************************************************************************/
+
+// String to use for formatting CSE numbers. Note that this is the positive number, e.g., from GET_CSE_INDEX().
+#define FMT_CSE "CSE #%02u"
public:
void optOptimizeValnumCSEs();
@@ -6882,16 +6917,15 @@ class Compiler
protected:
void optValnumCSE_Init();
unsigned optValnumCSE_Index(GenTree* tree, Statement* stmt);
- unsigned optValnumCSE_Locate();
- void optValnumCSE_InitDataFlow();
- void optValnumCSE_DataFlow();
- void optValnumCSE_Availablity();
- void optValnumCSE_Heuristic();
+ bool optValnumCSE_Locate();
+ void optValnumCSE_InitDataFlow();
+ void optValnumCSE_DataFlow();
+ void optValnumCSE_Availablity();
+ void optValnumCSE_Heuristic();
bool optDoCSE; // True when we have found a duplicate CSE tree
- bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase
- unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum
- unsigned optCSECandidateCount; // Count of CSE's candidates, reset for Lexical and ValNum CSE's
+ bool optValnumCSE_phase; // True when we are executing the optOptimizeValnumCSEs() phase
+ unsigned optCSECandidateCount; // Count of CSE's candidates
unsigned optCSEstart; // The first local variable number that is a CSE
unsigned optCSEcount; // The total count of CSE's introduced.
BasicBlock::weight_t optCSEweight; // The weight of the current block when we are doing PerformCSE
@@ -6916,6 +6950,7 @@ class Compiler
bool optConfigDisableCSE();
bool optConfigDisableCSE2();
#endif
+
void optOptimizeCSEs();
struct isVarAssgDsc
@@ -9337,6 +9372,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
bool disasmWithGC; // Display GC info interleaved with disassembly.
bool disDiffable; // Makes the Disassembly code 'diff-able'
bool disAddr; // Display process address next to each instruction in disassembly code
+ bool disAlignment; // Display alignment boundaries in disassembly code
bool disAsm2; // Display native code after it is generated using external disassembler
bool dspOrder; // Display names of each of the methods that we ngen/jit
bool dspUnwind; // Display the unwind info output
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index d05bfc6bbeb9f..ca9626ee11c1f 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -763,25 +763,6 @@ inline double getR8LittleEndian(const BYTE* ptr)
return *(double*)&val;
}
-/*****************************************************************************
- *
- * Return the normalized index to use in the EXPSET_TP for the CSE with
- * the given CSE index.
- * Each GenTree has the following field:
- * signed char gtCSEnum; // 0 or the CSE index (negated if def)
- * So zero is reserved to mean this node is not a CSE
- * and postive values indicate CSE uses and negative values indicate CSE defs.
- * The caller of this method must pass a non-zero postive value.
- * This precondition is checked by the assert on the first line of this method.
- */
-
-inline unsigned int genCSEnum2bit(unsigned index)
-{
- assert((index > 0) && (index <= EXPSET_SZ));
-
- return (index - 1);
-}
-
#ifdef DEBUG
const char* genES2str(BitVecTraits* traits, EXPSET_TP set);
const char* refCntWtd2str(BasicBlock::weight_t refCntWtd);
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index 913951568fd1b..21e37879eed7e 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -773,7 +773,7 @@ insGroup* emitter::emitSavIG(bool emitAdd)
memcpy(id, emitCurIGfreeBase, sz);
#ifdef DEBUG
- if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false)
+ if (false && emitComp->verbose) // this is not useful in normal dumps (hence it is normally under if (false))
{
// If there's an error during emission, we may want to connect the post-copy address
// of an instrDesc with the pre-copy address (the one that was originally created). This
@@ -843,7 +843,7 @@ insGroup* emitter::emitSavIG(bool emitAdd)
#ifdef DEBUG
if (emitComp->opts.dspCode)
{
- printf("\n G_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum);
+ printf("\n %s:", emitLabelString(ig));
if (emitComp->verbose)
{
printf(" ; offs=%06XH, funclet=%02u, bbWeight=%s", ig->igOffs, ig->igFuncIdx,
@@ -1184,7 +1184,7 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const
//----------------------------------------------------------------------------------------
// insEvaluateExecutionCost:
-// Returns the estimate execution cost fortyhe current instruction
+// Returns the estimated execution cost for the current instruction
//
// Arguments:
// id - The current instruction descriptor to be evaluated
@@ -1193,8 +1193,6 @@ int emitter::instrDesc::idAddrUnion::iiaGetJitDataOffset() const
// calls getInsExecutionCharacteristics and uses the result
// to compute an estimated execution cost
//
-// Notes:
-//
float emitter::insEvaluateExecutionCost(instrDesc* id)
{
insExecutionCharacteristics result = getInsExecutionCharacteristics(id);
@@ -1202,8 +1200,10 @@ float emitter::insEvaluateExecutionCost(instrDesc* id)
float latency = result.insLatency;
unsigned memAccessKind = result.insMemoryAccessKind;
- // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL
- assert(throughput > 0.0);
+ // Check for PERFSCORE_THROUGHPUT_ILLEGAL and PERFSCORE_LATENCY_ILLEGAL.
+ // Note that 0.0 throughput is allowed for pseudo-instructions in the instrDesc list that won't actually
+ // generate code.
+ assert(throughput >= 0.0);
assert(latency >= 0.0);
if (memAccessKind == PERFSCORE_MEMORY_WRITE)
@@ -1241,7 +1241,7 @@ float emitter::insEvaluateExecutionCost(instrDesc* id)
void emitter::perfScoreUnhandledInstruction(instrDesc* id, insExecutionCharacteristics* pResult)
{
#ifdef DEBUG
- printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsName(id->idIns()),
+ printf("PerfScore: unhandled instruction: %s, format %s", codeGen->genInsDisplayName(id),
emitIfName(id->idInsFmt()));
assert(!"PerfScore: unhandled instruction");
#endif
@@ -1524,6 +1524,14 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz)
emitCurIGinsCnt++;
+#ifdef DEBUG
+ if (emitComp->compCurBB != emitCurIG->lastGeneratedBlock)
+ {
+ emitCurIG->igBlocks.push_back(emitComp->compCurBB);
+ emitCurIG->lastGeneratedBlock = emitComp->compCurBB;
+ }
+#endif // DEBUG
+
return id;
}
@@ -2504,7 +2512,7 @@ bool emitter::emitNoGChelper(CORINFO_METHOD_HANDLE methHnd)
void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- bool isFinallyTarget DEBUG_ARG(unsigned bbNum))
+ bool isFinallyTarget DEBUG_ARG(BasicBlock* block))
{
/* Create a new IG if the current one is non-empty */
@@ -2533,7 +2541,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars,
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
#ifdef DEBUG
- JITDUMP("Mapped " FMT_BB " to G_M%03u_IG%02u\n", bbNum, emitComp->compMethodID, emitCurIG->igNum);
+ JITDUMP("Mapped " FMT_BB " to %s\n", block->bbNum, emitLabelString(emitCurIG));
if (EMIT_GC_VERBOSE)
{
@@ -2548,6 +2556,7 @@ void* emitter::emitAddLabel(VARSET_VALARG_TP GCvars,
printf("\n");
}
#endif
+
return emitCurIG;
}
@@ -2561,6 +2570,40 @@ void* emitter::emitAddInlineLabel()
return emitCurIG;
}
+#ifdef DEBUG
+
+//-----------------------------------------------------------------------------
+// emitPrintLabel: Print the assembly label for an insGroup. We could use emitter::emitLabelString()
+// to be consistent, but that seems silly.
+//
+void emitter::emitPrintLabel(insGroup* ig)
+{
+ printf("G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum);
+}
+
+//-----------------------------------------------------------------------------
+// emitLabelString: Return label string for an insGroup, for use in debug output.
+// This can be called up to four times in a single 'printf' before the static buffers
+// get reused.
+//
+// Returns:
+// String with insGroup label
+//
+const char* emitter::emitLabelString(insGroup* ig)
+{
+ const int TEMP_BUFFER_LEN = 40;
+ static unsigned curBuf = 0;
+ static char buf[4][TEMP_BUFFER_LEN];
+ const char* retbuf;
+
+ sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum);
+ retbuf = buf[curBuf];
+ curBuf = (curBuf + 1) % 4;
+ return retbuf;
+}
+
+#endif // DEBUG
+
#ifdef TARGET_ARMARCH
// Does the argument location point to an IG at the end of a function or funclet?
@@ -3502,7 +3545,7 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose)
const int TEMP_BUFFER_LEN = 40;
char buff[TEMP_BUFFER_LEN];
- sprintf_s(buff, TEMP_BUFFER_LEN, "G_M%03u_IG%02u: ", emitComp->compMethodID, ig->igNum);
+ sprintf_s(buff, TEMP_BUFFER_LEN, "%s: ", emitLabelString(ig));
printf("%s; ", buff);
// We dump less information when we're only interleaving GC info with a disassembly listing,
@@ -3599,9 +3642,10 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose)
else
{
const char* separator = "";
+
if (jitdump)
{
- printf("offs=%06XH, size=%04XH", ig->igOffs, ig->igSize);
+ printf("%soffs=%06XH, size=%04XH", separator, ig->igOffs, ig->igSize);
separator = ", ";
}
@@ -3642,6 +3686,15 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose)
}
#endif // FEATURE_LOOP_ALIGN
+ if (jitdump && !ig->igBlocks.empty())
+ {
+ for (auto block : ig->igBlocks)
+ {
+ printf("%s%s", separator, block->dspToString());
+ separator = ", ";
+ }
+ }
+
emitDispIGflags(ig->igFlags);
if (ig == emitCurIG)
@@ -3733,10 +3786,6 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp)
{
size_t is;
-#ifdef DEBUG
- size_t beforeAddr = (size_t)*dp;
-#endif
-
/* Record the beginning offset of the instruction */
BYTE* curInsAdr = *dp;
@@ -3822,52 +3871,7 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp)
id->idDebugOnlyInfo()->idNum, is, emitSizeOfInsDsc(id));
assert(is == emitSizeOfInsDsc(id));
}
-
- // Print the alignment boundary
- if ((emitComp->opts.disAsm || emitComp->verbose) && emitComp->opts.disAddr)
- {
- size_t currAddr = (size_t)*dp;
- size_t lastBoundaryAddr = currAddr & ~((size_t)emitComp->opts.compJitAlignLoopBoundary - 1);
-
- // draw boundary if beforeAddr was before the lastBoundary.
- if (beforeAddr < lastBoundaryAddr)
- {
- printf("; ");
- instruction currIns = id->idIns();
-
-#if defined(TARGET_XARCH)
-
- // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf
- bool isJccAffectedIns =
- ((currIns >= INS_i_jmp && currIns < INS_align) || (currIns == INS_call) || (currIns == INS_ret));
-
- instrDesc* nextId = id;
- castto(nextId, BYTE*) += is;
- instruction nextIns = nextId->idIns();
- if ((currIns == INS_cmp) || (currIns == INS_test) || (currIns == INS_add) || (currIns == INS_sub) ||
- (currIns == INS_and) || (currIns == INS_inc) || (currIns == INS_dec))
- {
- isJccAffectedIns |= (nextIns >= INS_i_jmp && nextIns < INS_align);
- }
-#else
- bool isJccAffectedIns = false;
-#endif
-
- // Indicate if instruction is at at 32B boundary or is splitted
- unsigned bytesCrossedBoundary = (currAddr & (emitComp->opts.compJitAlignLoopBoundary - 1));
- if ((bytesCrossedBoundary != 0) || (isJccAffectedIns && bytesCrossedBoundary == 0))
- {
- printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)", codeGen->genInsName(id->idIns()),
- bytesCrossedBoundary);
- }
- else
- {
- printf("...............................");
- }
- printf(" %dB boundary ...............................\n", (emitComp->opts.compJitAlignLoopBoundary));
- }
- }
-#endif
+#endif // DEBUG
return is;
}
@@ -4229,7 +4233,7 @@ void emitter::emitJumpDistBind()
{
if (tgtIG)
{
- printf(" to G_M%03u_IG%02u\n", emitComp->compMethodID, tgtIG->igNum);
+ printf(" to %s\n", emitLabelString(tgtIG));
}
else
{
@@ -4687,8 +4691,7 @@ void emitter::emitLoopAlignment()
// all IGs that follows this IG and participate in a loop.
emitCurIG->igFlags |= IGF_LOOP_ALIGN;
- JITDUMP("Adding 'align' instruction of %d bytes in G_M%03u_IG%02u.\n", paddingBytes, emitComp->compMethodID,
- emitCurIG->igNum);
+ JITDUMP("Adding 'align' instruction of %d bytes in %s.\n", paddingBytes, emitLabelString(emitCurIG));
#ifdef DEBUG
emitComp->loopAlignCandidates++;
@@ -5027,10 +5030,10 @@ void emitter::emitLoopAlignAdjustments()
alignInstr = prevAlignInstr;
}
- JITDUMP("Adjusted alignment of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum,
- estimatedPaddingNeeded, actualPaddingNeeded);
- JITDUMP("Adjusted size of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum,
- (alignIG->igSize + diff), alignIG->igSize);
+ JITDUMP("Adjusted alignment of %s from %u to %u.\n", emitLabelString(alignIG), estimatedPaddingNeeded,
+ actualPaddingNeeded);
+ JITDUMP("Adjusted size of %s from %u to %u.\n", emitLabelString(alignIG), (alignIG->igSize + diff),
+ alignIG->igSize);
}
// Adjust the offset of all IGs starting from next IG until we reach the IG having the next
@@ -5039,8 +5042,8 @@ void emitter::emitLoopAlignAdjustments()
insGroup* adjOffUptoIG = alignInstr->idaNext != nullptr ? alignInstr->idaNext->idaIG : emitIGlast;
while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum))
{
- JITDUMP("Adjusted offset of G_M%03u_IG%02u from %04X to %04X\n", emitComp->compMethodID, adjOffIG->igNum,
- adjOffIG->igOffs, (adjOffIG->igOffs - alignBytesRemoved));
+ JITDUMP("Adjusted offset of %s from %04X to %04X\n", emitLabelString(adjOffIG), adjOffIG->igOffs,
+ (adjOffIG->igOffs - alignBytesRemoved));
adjOffIG->igOffs -= alignBytesRemoved;
adjOffIG = adjOffIG->igNext;
}
@@ -5099,8 +5102,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
// No padding if loop is already aligned
if ((offset & (alignmentBoundary - 1)) == 0)
{
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %dB boundary.'\n",
- emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary);
+ JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %dB boundary.'\n", emitLabelString(ig->igNext),
+ alignmentBoundary);
return 0;
}
@@ -5124,8 +5127,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
// No padding if loop is big
if (loopSize > maxLoopSize)
{
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is big. LoopSize= %d, MaxLoopSize= %d.'\n",
- emitComp->compMethodID, ig->igNext->igNum, loopSize, maxLoopSize);
+ JITDUMP(";; Skip alignment: 'Loop at %s is big. LoopSize= %d, MaxLoopSize= %d.'\n", emitLabelString(ig->igNext),
+ loopSize, maxLoopSize);
return 0;
}
@@ -5151,17 +5154,16 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
if (nPaddingBytes == 0)
{
skipPadding = true;
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u already aligned at %uB boundary.'\n",
- emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary);
+ JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %uB boundary.'\n",
+ emitLabelString(ig->igNext), alignmentBoundary);
}
// Check if the alignment exceeds new maxPadding limit
else if (nPaddingBytes > nMaxPaddingBytes)
{
skipPadding = true;
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, "
+ JITDUMP(";; Skip alignment: 'Loop at %s PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, "
"AlignmentBoundary= %dB.'\n",
- emitComp->compMethodID, ig->igNext->igNum, nPaddingBytes, nMaxPaddingBytes, loopSize,
- alignmentBoundary);
+ emitLabelString(ig->igNext), nPaddingBytes, nMaxPaddingBytes, loopSize, alignmentBoundary);
}
}
@@ -5183,8 +5185,8 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
else
{
// Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment.
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n",
- emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary);
+ JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n",
+ emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary);
}
}
}
@@ -5212,13 +5214,13 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
else
{
// Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment.
- JITDUMP(";; Skip alignment: 'Loop at G_M%03u_IG%02u is aligned to fit in %d blocks of %d chunks.'\n",
- emitComp->compMethodID, ig->igNext->igNum, minBlocksNeededForLoop, alignmentBoundary);
+ JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n",
+ emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary);
}
}
- JITDUMP(";; Calculated padding to add %d bytes to align G_M%03u_IG%02u at %dB boundary.\n", paddingToAdd,
- emitComp->compMethodID, ig->igNext->igNum, alignmentBoundary);
+ JITDUMP(";; Calculated padding to add %d bytes to align %s at %dB boundary.\n", paddingToAdd,
+ emitLabelString(ig->igNext), alignmentBoundary);
// Either no padding is added because it is too expensive or the offset gets aligned
// to the alignment boundary
@@ -5900,7 +5902,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
}
else
{
- printf("\nG_M%03u_IG%02u:", emitComp->compMethodID, ig->igNum);
+ printf("\n%s:", emitLabelString(ig));
if (!emitComp->opts.disDiffable)
{
printf(" ;; offset=%04XH", emitCurCodeOffs(cp));
@@ -6017,9 +6019,97 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
emitCurIG = ig;
- for (unsigned cnt = ig->igInsCnt; cnt; cnt--)
+ for (unsigned cnt = ig->igInsCnt; cnt > 0; cnt--)
{
+#ifdef DEBUG
+ size_t curInstrAddr = (size_t)cp;
+ instrDesc* curInstrDesc = id;
+#endif
+
castto(id, BYTE*) += emitIssue1Instr(ig, id, &cp);
+
+#ifdef DEBUG
+ // Print the alignment boundary
+ if ((emitComp->opts.disAsm || emitComp->verbose) && (emitComp->opts.disAddr || emitComp->opts.disAlignment))
+ {
+ size_t afterInstrAddr = (size_t)cp;
+ instruction curIns = curInstrDesc->idIns();
+ bool isJccAffectedIns = false;
+
+#if defined(TARGET_XARCH)
+
+ // Determine if this instruction is part of a set that matches the Intel jcc erratum characteristic
+ // described here:
+ // https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf
+ // This is the case when a jump instruction crosses a 32-byte boundary, or ends on a 32-byte boundary.
+ // "Jump instruction" in this case includes conditional jump (jcc), macro-fused op-jcc (where 'op' is
+ // one of cmp, test, add, sub, and, inc, or dec), direct unconditional jump, indirect jump,
+ // direct/indirect call, and return.
+
+ size_t jccAlignBoundary = 32;
+ size_t jccAlignBoundaryMask = jccAlignBoundary - 1;
+ size_t jccLastBoundaryAddr = afterInstrAddr & ~jccAlignBoundaryMask;
+
+ if (curInstrAddr < jccLastBoundaryAddr)
+ {
+ isJccAffectedIns = IsJccInstruction(curIns) || IsJmpInstruction(curIns) || (curIns == INS_call) ||
+ (curIns == INS_ret);
+
+ // For op-Jcc there are two cases: (1) curIns is the jcc, in which case the above condition
+ // already covers us. (2) curIns is the `op` and the next instruction is the `jcc`. Note that
+ // we will never have a `jcc` as the first instruction of a group, so we don't need to worry
+ // about looking ahead to the next group after a an `op` of `op-Jcc`.
+
+ if (!isJccAffectedIns && (cnt > 1))
+ {
+ // The current `id` is valid, namely, there is another instruction in this group.
+ instruction nextIns = id->idIns();
+ if (((curIns == INS_cmp) || (curIns == INS_test) || (curIns == INS_add) ||
+ (curIns == INS_sub) || (curIns == INS_and) || (curIns == INS_inc) ||
+ (curIns == INS_dec)) &&
+ IsJccInstruction(nextIns))
+ {
+ isJccAffectedIns = true;
+ }
+ }
+
+ if (isJccAffectedIns)
+ {
+ unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & jccAlignBoundaryMask);
+ printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d ; jcc erratum) %dB boundary "
+ "...............................\n",
+ codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary, jccAlignBoundary);
+ }
+ }
+
+#endif // TARGET_XARCH
+
+ // Jcc affected instruction boundaries were printed above; handle other cases here.
+ if (!isJccAffectedIns)
+ {
+ size_t alignBoundaryMask = (size_t)emitComp->opts.compJitAlignLoopBoundary - 1;
+ size_t lastBoundaryAddr = afterInstrAddr & ~alignBoundaryMask;
+
+ // draw boundary if beforeAddr was before the lastBoundary.
+ if (curInstrAddr < lastBoundaryAddr)
+ {
+ // Indicate if instruction is at the alignment boundary or is split
+ unsigned bytesCrossedBoundary = (unsigned)(afterInstrAddr & alignBoundaryMask);
+ if (bytesCrossedBoundary != 0)
+ {
+ printf("; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (%s: %d)",
+ codeGen->genInsDisplayName(curInstrDesc), bytesCrossedBoundary);
+ }
+ else
+ {
+ printf("; ...............................");
+ }
+ printf(" %dB boundary ...............................\n",
+ emitComp->opts.compJitAlignLoopBoundary);
+ }
+ }
+ }
+#endif // DEBUG
}
#ifdef DEBUG
@@ -6862,11 +6952,8 @@ void emitter::emitDispDataSec(dataSecDsc* section)
BasicBlock* block = reinterpret_cast(data->dsCont)[i];
insGroup* ig = static_cast(emitCodeGetCookie(block));
- const char* blockLabelFormat = "G_M%03u_IG%02u";
- char blockLabel[64];
- char firstLabel[64];
- sprintf_s(blockLabel, _countof(blockLabel), blockLabelFormat, emitComp->compMethodID, ig->igNum);
- sprintf_s(firstLabel, _countof(firstLabel), blockLabelFormat, emitComp->compMethodID, igFirst->igNum);
+ const char* blockLabel = emitLabelString(ig);
+ const char* firstLabel = emitLabelString(igFirst);
if (isRelative)
{
@@ -7986,11 +8073,6 @@ insGroup* emitter::emitAllocIG()
ig->igSelf = ig;
#endif
-#if defined(DEBUG) || defined(LATE_DISASM)
- ig->igWeight = getCurrentBlockWeight();
- ig->igPerfScore = 0.0;
-#endif
-
#if EMITTER_STATS
emitTotalIGcnt += 1;
emitTotalIGsize += sz;
@@ -8028,6 +8110,11 @@ void emitter::emitInitIG(insGroup* ig)
ig->igFlags = 0;
+#if defined(DEBUG) || defined(LATE_DISASM)
+ ig->igWeight = getCurrentBlockWeight();
+ ig->igPerfScore = 0.0;
+#endif
+
/* Zero out some fields to avoid printing garbage in JitDumps. These
really only need to be set in DEBUG, but do it in all cases to make
sure we act the same in non-DEBUG builds.
@@ -8040,6 +8127,12 @@ void emitter::emitInitIG(insGroup* ig)
#if FEATURE_LOOP_ALIGN
ig->igLoopBackEdge = nullptr;
#endif
+
+#ifdef DEBUG
+ ig->lastGeneratedBlock = nullptr;
+ // Explicitly call init, since IGs don't actually have a constructor.
+ ig->igBlocks.jitstd::list::init(emitComp->getAllocator(CMK_LoopOpt));
+#endif
}
/*****************************************************************************
@@ -8104,6 +8197,11 @@ void emitter::emitNxtIG(bool extend)
// We've created a new IG; no need to force another one.
emitForceNewIG = false;
+
+#ifdef DEBUG
+ // We haven't written any code into the IG yet, so clear our record of the last block written to the IG.
+ emitCurIG->lastGeneratedBlock = nullptr;
+#endif
}
/*****************************************************************************
@@ -8638,7 +8736,7 @@ const char* emitter::emitOffsetToLabel(unsigned offs)
if (ig->igOffs == offs)
{
// Found it!
- sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "G_M%03u_IG%02u", emitComp->compMethodID, ig->igNum);
+ sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "%s", emitLabelString(ig));
retbuf = buf[curBuf];
curBuf = (curBuf + 1) % 4;
return retbuf;
diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h
index 2748acf463766..ef67148ea962a 100644
--- a/src/coreclr/jit/emit.h
+++ b/src/coreclr/jit/emit.h
@@ -247,6 +247,11 @@ struct insGroup
double igPerfScore; // The PerfScore for this insGroup
#endif
+#ifdef DEBUG
+ BasicBlock* lastGeneratedBlock; // The last block that generated code into this insGroup.
+ jitstd::list igBlocks; // All the blocks that generated code into this insGroup.
+#endif
+
UNATIVE_OFFSET igNum; // for ordering (and display) purposes
UNATIVE_OFFSET igOffs; // offset of this group within method
unsigned int igFuncIdx; // Which function/funclet does this belong to? (Index into Compiler::compFuncInfos array.)
@@ -1229,6 +1234,8 @@ class emitter
#define PERFSCORE_THROUGHPUT_ILLEGAL -1024.0f
+#define PERFSCORE_THROUGHPUT_ZERO 0.0f // Only used for pseudo-instructions that don't generate code
+
#define PERFSCORE_THROUGHPUT_6X (1.0f / 6.0f) // Hextuple issue
#define PERFSCORE_THROUGHPUT_5X 0.20f // Pentuple issue
#define PERFSCORE_THROUGHPUT_4X 0.25f // Quad issue
@@ -1902,13 +1909,18 @@ class emitter
void* emitAddLabel(VARSET_VALARG_TP GCvars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- bool isFinallyTarget = false DEBUG_ARG(unsigned bbNum = 0));
+ bool isFinallyTarget = false DEBUG_ARG(BasicBlock* block = nullptr));
// Same as above, except the label is added and is conceptually "inline" in
// the current block. Thus it extends the previous block and the emitter
// continues to track GC info as if there was no label.
void* emitAddInlineLabel();
+#ifdef DEBUG
+ void emitPrintLabel(insGroup* ig);
+ const char* emitLabelString(insGroup* ig);
+#endif
+
#ifdef TARGET_ARMARCH
void emitGetInstrDescs(insGroup* ig, instrDesc** id, int* insCnt);
diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp
index 6953df5b49a7c..5b01adbd14a5d 100644
--- a/src/coreclr/jit/emitarm.cpp
+++ b/src/coreclr/jit/emitarm.cpp
@@ -7292,7 +7292,7 @@ void emitter::emitDispInsHelp(
lab = (insGroup*)emitCodeGetCookie(*bbp++);
assert(lab);
- printf("\n DD G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum);
+ printf("\n DD %s", emitLabelString(lab));
} while (--cnt);
}
}
@@ -7601,7 +7601,7 @@ void emitter::emitDispInsHelp(
case IF_T2_M1: // Load Label
emitDispReg(id->idReg1(), attr, true);
if (id->idIsBound())
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
else
printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum);
break;
@@ -7646,7 +7646,7 @@ void emitter::emitDispInsHelp(
}
}
else if (id->idIsBound())
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
else
printf("L_M%03u_" FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum);
}
diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp
index 37e19a219d265..84c49d94723d4 100644
--- a/src/coreclr/jit/emitarm64.cpp
+++ b/src/coreclr/jit/emitarm64.cpp
@@ -12286,7 +12286,7 @@ void emitter::emitDispIns(
}
else if (id->idIsBound())
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
else
{
@@ -12324,7 +12324,7 @@ void emitter::emitDispIns(
emitDispReg(id->idReg1(), size, true);
if (id->idIsBound())
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
else
{
@@ -12338,7 +12338,7 @@ void emitter::emitDispIns(
emitDispImm(emitGetInsSC(id), true);
if (id->idIsBound())
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
else
{
@@ -12463,7 +12463,7 @@ void emitter::emitDispIns(
}
else if (id->idIsBound())
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
else
{
diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp
index 217f5f15aafb2..c35a6675fe757 100644
--- a/src/coreclr/jit/emitxarch.cpp
+++ b/src/coreclr/jit/emitxarch.cpp
@@ -24,37 +24,37 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "emit.h"
#include "codegen.h"
-bool IsSSEInstruction(instruction ins)
+bool emitter::IsSSEInstruction(instruction ins)
{
return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_SSE_INSTRUCTION);
}
-bool IsSSEOrAVXInstruction(instruction ins)
+bool emitter::IsSSEOrAVXInstruction(instruction ins)
{
return (ins >= INS_FIRST_SSE_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION);
}
-bool IsAVXOnlyInstruction(instruction ins)
+bool emitter::IsAVXOnlyInstruction(instruction ins)
{
return (ins >= INS_FIRST_AVX_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION);
}
-bool IsFMAInstruction(instruction ins)
+bool emitter::IsFMAInstruction(instruction ins)
{
return (ins >= INS_FIRST_FMA_INSTRUCTION) && (ins <= INS_LAST_FMA_INSTRUCTION);
}
-bool IsAVXVNNIInstruction(instruction ins)
+bool emitter::IsAVXVNNIInstruction(instruction ins)
{
return (ins >= INS_FIRST_AVXVNNI_INSTRUCTION) && (ins <= INS_LAST_AVXVNNI_INSTRUCTION);
}
-bool IsBMIInstruction(instruction ins)
+bool emitter::IsBMIInstruction(instruction ins)
{
return (ins >= INS_FIRST_BMI_INSTRUCTION) && (ins <= INS_LAST_BMI_INSTRUCTION);
}
-regNumber getBmiRegNumber(instruction ins)
+regNumber emitter::getBmiRegNumber(instruction ins)
{
switch (ins)
{
@@ -81,7 +81,7 @@ regNumber getBmiRegNumber(instruction ins)
}
}
-regNumber getSseShiftRegNumber(instruction ins)
+regNumber emitter::getSseShiftRegNumber(instruction ins)
{
switch (ins)
{
@@ -123,7 +123,7 @@ regNumber getSseShiftRegNumber(instruction ins)
}
}
-bool emitter::IsAVXInstruction(instruction ins)
+bool emitter::IsAVXInstruction(instruction ins) const
{
return UseVEXEncoding() && IsSSEOrAVXInstruction(ins);
}
@@ -445,7 +445,7 @@ bool emitter::Is4ByteSSEInstruction(instruction ins)
// Returns true if this instruction requires a VEX prefix
// All AVX instructions require a VEX prefix
-bool emitter::TakesVexPrefix(instruction ins)
+bool emitter::TakesVexPrefix(instruction ins) const
{
// special case vzeroupper as it requires 2-byte VEX prefix
// special case the fencing, movnti and the prefetch instructions as they never take a VEX prefix
@@ -521,7 +521,7 @@ emitter::code_t emitter::AddVexPrefix(instruction ins, code_t code, emitAttr att
}
// Returns true if this instruction, for the given EA_SIZE(attr), will require a REX.W prefix
-bool TakesRexWPrefix(instruction ins, emitAttr attr)
+bool emitter::TakesRexWPrefix(instruction ins, emitAttr attr)
{
// Because the current implementation of AVX does not have a way to distinguish between the register
// size specification (128 vs. 256 bits) and the operand size specification (32 vs. 64 bits), where both are
@@ -4299,6 +4299,38 @@ bool emitter::IsMovInstruction(instruction ins)
}
}
+//------------------------------------------------------------------------
+// IsJccInstruction: Determine if an instruction is a conditional jump instruction.
+//
+// Arguments:
+// ins -- The instruction being checked
+//
+// Return Value:
+// true if the instruction qualifies; otherwise, false
+//
+bool emitter::IsJccInstruction(instruction ins)
+{
+ return ((ins >= INS_jo) && (ins <= INS_jg)) || ((ins >= INS_l_jo) && (ins <= INS_l_jg));
+}
+
+//------------------------------------------------------------------------
+// IsJmpInstruction: Determine if an instruction is a jump instruction but NOT a conditional jump instruction.
+//
+// Arguments:
+// ins -- The instruction being checked
+//
+// Return Value:
+// true if the instruction qualifies; otherwise, false
+//
+bool emitter::IsJmpInstruction(instruction ins)
+{
+ return
+#ifdef TARGET_AMD64
+ (ins == INS_rex_jmp) ||
+#endif
+ (ins == INS_i_jmp) || (ins == INS_jmp) || (ins == INS_l_jmp);
+}
+
//----------------------------------------------------------------------------------------
// IsRedundantMov:
// Check if the current `mov` instruction is redundant and can be omitted.
@@ -8459,7 +8491,7 @@ void emitter::emitDispAddrMode(instrDesc* id, bool noDetail)
lab = (insGroup*)emitCodeGetCookie(*bbp++);
assert(lab);
- printf("\n D" SIZE_LETTER " G_M%03u_IG%02u", emitComp->compMethodID, lab->igNum);
+ printf("\n D" SIZE_LETTER " %s", emitLabelString(lab));
} while (--cnt);
}
}
@@ -8691,22 +8723,16 @@ void emitter::emitDispIns(
/* Display the instruction name */
- sstr = codeGen->genInsName(ins);
+ sstr = codeGen->genInsDisplayName(id);
+ printf(" %-9s", sstr);
- if (IsAVXInstruction(ins) && !IsBMIInstruction(ins))
- {
- printf(" v%-8s", sstr);
- }
- else
- {
- printf(" %-9s", sstr);
- }
#ifndef HOST_UNIX
- if (strnlen_s(sstr, 10) >= 8)
+ if (strnlen_s(sstr, 10) >= 9)
#else // HOST_UNIX
- if (strnlen(sstr, 10) >= 8)
+ if (strnlen(sstr, 10) >= 9)
#endif // HOST_UNIX
{
+ // Make sure there's at least one space after the instruction name, for very long instruction names.
printf(" ");
}
@@ -9656,7 +9682,7 @@ void emitter::emitDispIns(
}
else
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
}
else
@@ -14676,6 +14702,17 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
switch (ins)
{
case INS_align:
+#if FEATURE_LOOP_ALIGN
+ if (id->idCodeSize() == 0)
+ {
+ // We're not going to generate any instruction, so it doesn't count for PerfScore.
+ result.insThroughput = PERFSCORE_THROUGHPUT_ZERO;
+ result.insLatency = PERFSCORE_LATENCY_ZERO;
+ break;
+ }
+#endif
+ FALLTHROUGH;
+
case INS_nop:
case INS_int3:
assert(memFmt == IF_NONE);
diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h
index 8260445686be0..f952a6f649f44 100644
--- a/src/coreclr/jit/emitxarch.h
+++ b/src/coreclr/jit/emitxarch.h
@@ -83,7 +83,15 @@ code_t insEncodeOpreg(instruction ins, regNumber reg, emitAttr size);
unsigned insSSval(unsigned scale);
-bool IsAVXInstruction(instruction ins);
+static bool IsSSEInstruction(instruction ins);
+static bool IsSSEOrAVXInstruction(instruction ins);
+static bool IsAVXOnlyInstruction(instruction ins);
+static bool IsFMAInstruction(instruction ins);
+static bool IsAVXVNNIInstruction(instruction ins);
+static bool IsBMIInstruction(instruction ins);
+static regNumber getBmiRegNumber(instruction ins);
+static regNumber getSseShiftRegNumber(instruction ins);
+bool IsAVXInstruction(instruction ins) const;
code_t insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, code_t code);
code_t AddRexWPrefix(instruction ins, code_t code);
@@ -98,6 +106,9 @@ static bool IsMovInstruction(instruction ins);
bool IsRedundantMov(
instruction ins, insFormat fmt, emitAttr size, regNumber dst, regNumber src, bool canIgnoreSideEffects);
+static bool IsJccInstruction(instruction ins);
+static bool IsJmpInstruction(instruction ins);
+
bool AreUpper32BitsZero(regNumber reg);
bool AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps treeOps);
@@ -116,7 +127,8 @@ bool hasRexPrefix(code_t code)
#define VEX_PREFIX_MASK_3BYTE 0xFF000000000000ULL
#define VEX_PREFIX_CODE_3BYTE 0xC4000000000000ULL
-bool TakesVexPrefix(instruction ins);
+bool TakesVexPrefix(instruction ins) const;
+static bool TakesRexWPrefix(instruction ins, emitAttr attr);
// Returns true if the instruction encoding already contains VEX prefix
bool hasVexPrefix(code_t code)
@@ -142,7 +154,7 @@ code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr
}
bool useVEXEncodings;
-bool UseVEXEncoding()
+bool UseVEXEncoding() const
{
return useVEXEncodings;
}
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index 62a9467da63fe..6c65586fbca1c 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -818,8 +818,24 @@ class FgStack
return false;
}
const unsigned argNum = value - SLOT_ARGUMENT;
- assert(argNum < info->argCnt);
- return info->inlArgInfo[argNum].argIsInvariant;
+ if (argNum < info->argCnt)
+ {
+ return info->inlArgInfo[argNum].argIsInvariant;
+ }
+ return false;
+ }
+ static bool IsExactArgument(FgSlot value, InlineInfo* info)
+ {
+ if ((info == nullptr) || !IsArgument(value))
+ {
+ return false;
+ }
+ const unsigned argNum = value - SLOT_ARGUMENT;
+ if (argNum < info->argCnt)
+ {
+ return info->inlArgInfo[argNum].argIsExact;
+ }
+ return false;
}
static unsigned SlotTypeToArgNum(FgSlot value)
{
@@ -876,6 +892,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
if (makeInlineObservations)
{
+ // Set default values for profile (to avoid NoteFailed in CALLEE_IL_CODE_SIZE's handler)
+ // these will be overridden later.
+ compInlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true);
+ compInlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, 1.0);
// Observe force inline state and code size.
compInlineResult->NoteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline);
compInlineResult->NoteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize);
@@ -1031,7 +1051,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
if (makeInlineObservations)
{
FgStack::FgSlot slot = pushedStack.Top();
- if (FgStack::IsConstantOrConstArg(slot, impInlineInfo))
+ if (FgStack::IsConstantOrConstArg(slot, impInlineInfo) ||
+ FgStack::IsExactArgument(slot, impInlineInfo))
{
compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR_UN);
handled = true; // and keep argument in the pushedStack
@@ -1338,44 +1359,59 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
FgStack::FgSlot arg0 = pushedStack.Top(1);
FgStack::FgSlot arg1 = pushedStack.Top(0);
- if ((FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo)) ||
- (FgStack::IsConstant(arg1) && FgStack::IsConstArgument(arg0, impInlineInfo)) ||
- (FgStack::IsConstArgument(arg0, impInlineInfo) &&
- FgStack::IsConstArgument(arg1, impInlineInfo)))
+ // Const op ConstArg -> ConstArg
+ if (FgStack::IsConstant(arg0) && FgStack::IsConstArgument(arg1, impInlineInfo))
{
// keep stack unchanged
handled = true;
compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR);
}
- if ((FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1)) ||
- (FgStack::IsConstant(arg1) && FgStack::IsConstant(arg0)))
+ // ConstArg op Const -> ConstArg
+ // ConstArg op ConstArg -> ConstArg
+ else if (FgStack::IsConstArgument(arg0, impInlineInfo) &&
+ FgStack::IsConstantOrConstArg(arg1, impInlineInfo))
+ {
+ if (FgStack::IsConstant(arg1))
+ {
+ pushedStack.Push(arg0);
+ }
+ handled = true;
+ compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_EXPR);
+ }
+ // Const op Const -> Const
+ else if (FgStack::IsConstant(arg0) && FgStack::IsConstant(arg1))
{
// both are constants, but we're mostly interested in cases where a const arg leads to
// a foldable expression.
handled = true;
}
+ // Arg op ConstArg
+ // Arg op Const
else if (FgStack::IsArgument(arg0) && FgStack::IsConstantOrConstArg(arg1, impInlineInfo))
{
// "Arg op CNS" --> keep arg0 in the stack for the next ops
+ pushedStack.Push(arg0);
handled = true;
compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS);
}
+ // ConstArg op Arg
+ // Const op Arg
else if (FgStack::IsArgument(arg1) && FgStack::IsConstantOrConstArg(arg0, impInlineInfo))
{
// "CNS op ARG" --> keep arg1 in the stack for the next ops
- pushedStack.Push(arg1);
handled = true;
compInlineResult->Note(InlineObservation::CALLEE_BINARY_EXRP_WITH_CNS);
}
-
+ // X / ConstArg
+ // X % ConstArg
if (FgStack::IsConstArgument(arg1, impInlineInfo))
{
- // Special case: "X / ConstArg" or "X % ConstArg"
if ((opcode == CEE_DIV) || (opcode == CEE_DIV_UN) || (opcode == CEE_REM) ||
(opcode == CEE_REM_UN))
{
compInlineResult->Note(InlineObservation::CALLSITE_DIV_BY_CNS);
}
+ pushedStack.Push(arg0);
handled = true;
}
}
@@ -1583,6 +1619,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
if (makeInlineObservations)
{
compInlineResult->Note(InlineObservation::CALLEE_HAS_SWITCH);
+ if (FgStack::IsConstantOrConstArg(pushedStack.Top(), impInlineInfo))
+ {
+ compInlineResult->Note(InlineObservation::CALLSITE_FOLDABLE_SWITCH);
+ }
// Fail fast, if we're inlining and can't handle this.
if (isInlining && compInlineResult->IsFailure())
diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp
index 3b1b1739919d5..92a82c346a545 100644
--- a/src/coreclr/jit/fgopt.cpp
+++ b/src/coreclr/jit/fgopt.cpp
@@ -1620,6 +1620,9 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext)
}
}
bNext->bbPreds = nullptr;
+
+ // `block` can no longer be a loop pre-header (if it was before).
+ block->bbFlags &= ~BBF_LOOP_PREHEADER;
}
else
{
diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp
index 1659b2d390529..31d7c208ebaf0 100644
--- a/src/coreclr/jit/fgprofile.cpp
+++ b/src/coreclr/jit/fgprofile.cpp
@@ -47,6 +47,31 @@ bool Compiler::fgHaveProfileData()
return (fgPgoSchema != nullptr);
}
+//------------------------------------------------------------------------
+// fgHaveSufficientProfileData: check if profile data is available
+// and is sufficient enough to be trustful.
+//
+// Returns:
+// true if so
+//
+// Note:
+// See notes for fgHaveProfileData.
+//
+bool Compiler::fgHaveSufficientProfileData()
+{
+ if (!fgHaveProfileData())
+ {
+ return false;
+ }
+
+ if ((fgFirstBB != nullptr) && (fgPgoSource == ICorJitInfo::PgoSource::Static))
+ {
+ const BasicBlock::weight_t sufficientSamples = 1000;
+ return fgFirstBB->bbWeight > sufficientSamples;
+ }
+ return true;
+}
+
//------------------------------------------------------------------------
// fgApplyProfileScale: scale inlinee counts by appropriate scale factor
//
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 9422225c5d3de..7f16e6cd40048 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -10294,7 +10294,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _
{
if (IS_CSE_INDEX(tree->gtCSEnum))
{
- printf("CSE #%02d (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def"));
+ printf(FMT_CSE " (%s)", GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def"));
}
else
{
@@ -14856,19 +14856,15 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
JITDUMP("\nFolding operator with constant nodes into a constant:\n");
DISPTREE(tree);
-#ifdef TARGET_64BIT
- // Some operations are performed as 64 bit instead of 32 bit so the upper 32 bits
- // need to be discarded. Since constant values are stored as ssize_t and the node
- // has TYP_INT the result needs to be sign extended rather than zero extended.
- i1 = INT32(i1);
-#endif // TARGET_64BIT
-
// Also all conditional folding jumps here since the node hanging from
// GT_JTRUE has to be a GT_CNS_INT - value 0 or 1.
tree->ChangeOperConst(GT_CNS_INT);
tree->ChangeType(TYP_INT);
- tree->AsIntCon()->SetIconValue(i1);
+ // Some operations are performed as 64 bit instead of 32 bit so the upper 32 bits
+ // need to be discarded. Since constant values are stored as ssize_t and the node
+ // has TYP_INT the result needs to be sign extended rather than zero extended.
+ tree->AsIntCon()->SetIconValue(static_cast(i1));
tree->AsIntCon()->gtFieldSeq = fieldSeq;
if (vnStore != nullptr)
{
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index c12e46f02bdab..4b7374573ef73 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -494,6 +494,7 @@ enum GenTreeFlags : unsigned int
GTF_INX_RNGCHK = 0x80000000, // GT_INDEX/GT_INDEX_ADDR -- the array reference should be range-checked.
GTF_INX_STRING_LAYOUT = 0x40000000, // GT_INDEX -- this uses the special string array layout
+ GTF_INX_NOFAULT = 0x20000000, // GT_INDEX -- the INDEX does not throw an exception (morph to GTF_IND_NONFAULTING)
GTF_IND_TGT_NOT_HEAP = 0x80000000, // GT_IND -- the target is not on the heap
GTF_IND_VOLATILE = 0x40000000, // GT_IND -- the load or store must use volatile sematics (this is a nop on X86)
@@ -6800,6 +6801,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
GenTreeCopyOrReload(genTreeOps oper, var_types type, GenTree* op1) : GenTreeUnOp(oper, type, op1)
{
+ assert(type != TYP_STRUCT || op1->IsMultiRegNode());
SetRegNum(REG_NA);
ClearOtherRegs();
}
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index d49a12ffd13dd..b3857cdec583a 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -13075,27 +13075,36 @@ void Compiler::impImportBlockCode(BasicBlock* block)
op2 = impPopStack().val;
op1 = impPopStack().val;
+ // Recognize the IL idiom of CGT_UN(op1, 0) and normalize
+ // it so that downstream optimizations don't have to.
+ if ((opcode == CEE_CGT_UN) && op2->IsIntegralConst(0))
+ {
+ oper = GT_NE;
+ uns = false;
+ }
+
#ifdef TARGET_64BIT
- if (varTypeIsI(op1->TypeGet()) && (genActualType(op2->TypeGet()) == TYP_INT))
+ // TODO-Casts: create a helper that upcasts int32 -> native int when necessary.
+ // See also identical code in impGetByRefResultType and STSFLD import.
+ if (varTypeIsI(op1) && (genActualType(op2) == TYP_INT))
{
- op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
+ op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, TYP_I_IMPL);
}
- else if (varTypeIsI(op2->TypeGet()) && (genActualType(op1->TypeGet()) == TYP_INT))
+ else if (varTypeIsI(op2) && (genActualType(op1) == TYP_INT))
{
- op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
+ op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, TYP_I_IMPL);
}
#endif // TARGET_64BIT
- assertImp(genActualType(op1->TypeGet()) == genActualType(op2->TypeGet()) ||
- (varTypeIsI(op1->TypeGet()) && varTypeIsI(op2->TypeGet())) ||
- (varTypeIsFloating(op1->gtType) && varTypeIsFloating(op2->gtType)));
+ assertImp(genActualType(op1) == genActualType(op2) || (varTypeIsI(op1) && varTypeIsI(op2)) ||
+ (varTypeIsFloating(op1) && varTypeIsFloating(op2)));
- /* Create the comparison node */
+ // Create the comparison node.
op1 = gtNewOperNode(oper, TYP_INT, op1, op2);
- /* TODO: setting both flags when only one is appropriate */
- if (opcode == CEE_CGT_UN || opcode == CEE_CLT_UN)
+ // TODO: setting both flags when only one is appropriate.
+ if (uns)
{
op1->gtFlags |= GTF_RELOP_NAN_UN | GTF_UNSIGNED;
}
@@ -13258,8 +13267,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
goto COND_JUMP;
case CEE_SWITCH:
- assert(!compIsForInlining());
-
if (tiVerificationNeeded)
{
Verify(impStackTop().seTypeInfo.IsType(TI_INT), "Bad switch val");
@@ -19071,33 +19078,30 @@ void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, I
inlineResult->NoteInt(InlineObservation::CALLSITE_FREQUENCY, static_cast(frequency));
inlineResult->NoteInt(InlineObservation::CALLSITE_WEIGHT, (int)(weight));
+ bool hasProfile = false;
+ double profileFreq = 0.0;
+
// If the call site has profile data, report the relative frequency of the site.
//
- if ((pInlineInfo != nullptr) && rootCompiler->fgHaveProfileData() && pInlineInfo->iciBlock->hasProfileWeight())
+ if ((pInlineInfo != nullptr) && rootCompiler->fgHaveSufficientProfileData())
{
- BasicBlock::weight_t callSiteWeight = pInlineInfo->iciBlock->bbWeight;
- BasicBlock::weight_t entryWeight = rootCompiler->fgFirstBB->bbWeight;
- BasicBlock::weight_t profileFreq = entryWeight == 0.0f ? 0.0f : callSiteWeight / entryWeight;
+ const BasicBlock::weight_t callSiteWeight = pInlineInfo->iciBlock->bbWeight;
+ const BasicBlock::weight_t entryWeight = rootCompiler->fgFirstBB->bbWeight;
+ profileFreq = entryWeight == 0.0f ? 0.0 : callSiteWeight / entryWeight;
+ hasProfile = true;
assert(callSiteWeight >= 0);
assert(entryWeight >= 0);
-
- BasicBlock::weight_t sufficientSamples = 1000.0f;
-
- if (!rootCompiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) ||
- ((callSiteWeight + entryWeight) > sufficientSamples))
- {
- // Let's not report profiles for methods with insufficient samples during prejitting.
- inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true);
- inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, profileFreq);
- }
}
- else if ((pInlineInfo == nullptr) && rootCompiler->fgHaveProfileData())
+ else if (pInlineInfo == nullptr)
{
// Simulate a hot callsite for PrejitRoot mode.
- inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, true);
- inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, 1.0);
+ hasProfile = true;
+ profileFreq = 1.0;
}
+
+ inlineResult->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE, hasProfile);
+ inlineResult->NoteDouble(InlineObservation::CALLSITE_PROFILE_FREQUENCY, profileFreq);
}
/*****************************************************************************
@@ -19243,6 +19247,10 @@ void Compiler::impCheckCanInline(GenTreeCall* call,
goto _exit;
}
+ // Profile data allows us to avoid early "too many IL bytes" outs.
+ pParam->result->NoteBool(InlineObservation::CALLSITE_HAS_PROFILE,
+ pParam->pThis->fgHaveSufficientProfileData());
+
bool forceInline;
forceInline = !!(pParam->methAttr & CORINFO_FLG_FORCEINLINE);
@@ -19465,6 +19473,10 @@ void Compiler::impInlineRecordArgInfo(InlineInfo* pInlineInfo,
}
}
+ bool isExact = false;
+ bool isNonNull = false;
+ inlCurArgInfo->argIsExact = (gtGetClassHandle(curArgVal, &isExact, &isNonNull) != NO_CLASS_HANDLE) && isExact;
+
// If the arg is a local that is address-taken, we can't safely
// directly substitute it into the inlinee.
//
diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def
index a16d82888b64a..cbd85ff240dea 100644
--- a/src/coreclr/jit/inline.def
+++ b/src/coreclr/jit/inline.def
@@ -182,6 +182,7 @@ INLINE_OBSERVATION(FOLDABLE_INTRINSIC, int, "foldable intrinsic",
INLINE_OBSERVATION(FOLDABLE_EXPR, int, "foldable binary expression", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FOLDABLE_EXPR_UN, int, "foldable unary expression", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FOLDABLE_BRANCH, int, "foldable branch", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(FOLDABLE_SWITCH, int, "foldable switch", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DIV_BY_CNS, int, "dividy by const", INFORMATION, CALLSITE)
INLINE_OBSERVATION(CONSTANT_ARG_FEEDS_TEST, bool, "constant argument feeds test", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DEPTH, int, "depth", INFORMATION, CALLSITE)
diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h
index bc08dd8110d2a..6a39b2d9cb5d0 100644
--- a/src/coreclr/jit/inline.h
+++ b/src/coreclr/jit/inline.h
@@ -610,6 +610,7 @@ struct InlArgInfo
unsigned argHasStargOp : 1; // Is there STARG(s) operation on this argument?
unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a
// field in them?
+ unsigned argIsExact : 1; // Is this arg of an exact class?
};
// InlLclVarInfo describes inline candidate argument and local variable properties.
diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp
index fdf4ae19341c2..e84ff2858a011 100644
--- a/src/coreclr/jit/inlinepolicy.cpp
+++ b/src/coreclr/jit/inlinepolicy.cpp
@@ -326,7 +326,6 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value)
m_ArgFeedsRangeCheck++;
break;
- case InlineObservation::CALLEE_HAS_SWITCH:
case InlineObservation::CALLEE_UNSUPPORTED_OPCODE:
propagate = true;
break;
@@ -1294,6 +1293,14 @@ void ExtendedDefaultPolicy::NoteBool(InlineObservation obs, bool value)
m_FoldableBranch++;
break;
+ case InlineObservation::CALLSITE_FOLDABLE_SWITCH:
+ m_FoldableSwitch++;
+ break;
+
+ case InlineObservation::CALLEE_HAS_SWITCH:
+ m_Switch++;
+ break;
+
case InlineObservation::CALLSITE_DIV_BY_CNS:
m_DivByCns++;
break;
@@ -1327,7 +1334,14 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value)
{
assert(m_IsForceInlineKnown);
assert(value != 0);
- m_CodeSize = static_cast(value);
+ m_CodeSize = static_cast(value);
+ unsigned maxCodeSize = static_cast(JitConfig.JitExtDefaultPolicyMaxIL());
+
+ // TODO: Enable for PgoSource::Static as well if it's not the generic profile we bundle.
+ if (m_HasProfile && (m_RootCompiler->fgPgoSource == ICorJitInfo::PgoSource::Dynamic))
+ {
+ maxCodeSize = static_cast(JitConfig.JitExtDefaultPolicyMaxILProf());
+ }
if (m_IsForceInline)
{
@@ -1339,7 +1353,7 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value)
// Candidate based on small size
SetCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE);
}
- else if (m_CodeSize <= (unsigned)JitConfig.JitExtDefaultPolicyMaxIL())
+ else if (m_CodeSize <= maxCodeSize)
{
// Candidate, pending profitability evaluation
SetCandidate(InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
@@ -1357,16 +1371,16 @@ void ExtendedDefaultPolicy::NoteInt(InlineObservation obs, int value)
{
SetNever(InlineObservation::CALLEE_DOES_NOT_RETURN);
}
- else if (!m_IsForceInline)
+ else if (!m_IsForceInline && !m_HasProfile)
{
unsigned bbLimit = (unsigned)JitConfig.JitExtDefaultPolicyMaxBB();
if (m_IsPrejitRoot)
{
// We're not able to recognize arg-specific foldable branches
// in prejit-root mode.
- bbLimit += 3;
+ bbLimit += 5 + m_Switch * 10;
}
- bbLimit += m_FoldableBranch;
+ bbLimit += m_FoldableBranch + m_FoldableSwitch * 10;
if ((unsigned)value > bbLimit)
{
SetNever(InlineObservation::CALLEE_TOO_MANY_BASIC_BLOCKS);
@@ -1419,13 +1433,13 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
if (m_ReturnsStructByValue)
{
// For structs-passed-by-value we might avoid expensive copy operations if we inline.
- multiplier += 1.5;
+ multiplier += 2.0;
JITDUMP("\nInline candidate returns a struct by value. Multiplier increased to %g.", multiplier);
}
else if (m_ArgIsStructByValue > 0)
{
// Same here
- multiplier += 1.5;
+ multiplier += 2.0;
JITDUMP("\n%d arguments are structs passed by value. Multiplier increased to %g.", m_ArgIsStructByValue,
multiplier);
}
@@ -1451,13 +1465,13 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
if (m_ArgFeedsRangeCheck > 0)
{
- multiplier += 0.5;
+ multiplier += 1.0;
JITDUMP("\nInline candidate has arg that feeds range check. Multiplier increased to %g.", multiplier);
}
if (m_NonGenericCallsGeneric)
{
- multiplier += 1.5;
+ multiplier += 2.0;
JITDUMP("\nInline candidate is generic and caller is not. Multiplier increased to %g.", multiplier);
}
@@ -1507,7 +1521,7 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
if (m_Intrinsic > 0)
{
// In most cases such intrinsics are lowered as single CPU instructions
- multiplier += 1.5;
+ multiplier += 1.0 + m_Intrinsic * 0.3;
JITDUMP("\nInline has %d intrinsics. Multiplier increased to %g.", m_Intrinsic, multiplier);
}
@@ -1636,6 +1650,28 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
break;
}
+ if (m_FoldableSwitch > 0)
+ {
+ multiplier += 6.0;
+ JITDUMP("\nInline candidate has %d foldable switches. Multiplier increased to %g.", m_FoldableSwitch,
+ multiplier);
+ }
+ else if (m_Switch > 0)
+ {
+ if (m_IsPrejitRoot)
+ {
+ // Assume the switches can be foldable in PrejitRoot mode.
+ multiplier += 6.0;
+ JITDUMP("\nPrejit root candidate has %d switches. Multiplier increased to %g.", m_Switch, multiplier);
+ }
+ else
+ {
+ // TODO: Investigate cases where it makes sense to inline non-foldable switches
+ multiplier = 0.0;
+ JITDUMP("\nInline candidate has %d switches. Multiplier limited to %g.", m_Switch, multiplier);
+ }
+ }
+
if (m_HasProfile)
{
// There are cases when Profile Data can be misleading or polluted:
@@ -1657,14 +1693,16 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
{
multiplier *= min(m_ProfileFrequency, 1.0) * profileScale;
}
- JITDUMP("\nCallsite has profile data: %g.", m_ProfileFrequency);
+ JITDUMP("\nCallsite has profile data: %g. Multiplier limited to %g.", m_ProfileFrequency, multiplier);
}
- if (m_RootCompiler->lvaTableCnt > ((unsigned)(JitConfig.JitMaxLocalsToTrack() / 4)))
+ // Slow down if there are already too many locals
+ if (m_RootCompiler->lvaTableCnt > 64)
{
- // Slow down inlining if we already have to many locals in the rootCompiler.
- multiplier /= ((double)m_RootCompiler->lvaTableCnt / ((double)JitConfig.JitMaxLocalsToTrack() / 4.0));
- JITDUMP("\nCaller %d locals. Multiplier decreased to %g.", m_RootCompiler->lvaTableCnt, multiplier);
+ // E.g. MaxLocalsToTrack = 1024 and lvaTableCnt = 512 -> multiplier *= 0.5;
+ const double lclFullness = min(1.0, (double)m_RootCompiler->lvaTableCnt / JitConfig.JitMaxLocalsToTrack());
+ multiplier *= (1.0 - lclFullness);
+ JITDUMP("\nCaller has %d locals. Multiplier decreased to %g.", m_RootCompiler->lvaTableCnt, multiplier);
}
if (m_BackwardJump)
@@ -1738,6 +1776,8 @@ void ExtendedDefaultPolicy::OnDumpXml(FILE* file, unsigned indent) const
XATTR_I4(m_FoldableExpr)
XATTR_I4(m_FoldableExprUn)
XATTR_I4(m_FoldableBranch)
+ XATTR_I4(m_FoldableSwitch)
+ XATTR_I4(m_Switch)
XATTR_I4(m_DivByCns)
XATTR_B(m_ReturnsStructByValue)
XATTR_B(m_IsFromValueClass)
@@ -1927,7 +1967,6 @@ void DiscretionaryPolicy::NoteDouble(InlineObservation obs, double value)
{
assert(obs == InlineObservation::CALLSITE_PROFILE_FREQUENCY);
assert(value >= 0.0);
- assert(m_ProfileFrequency == 0.0);
m_ProfileFrequency = value;
}
diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h
index 7d0d83bd73646..466e17fe0e112 100644
--- a/src/coreclr/jit/inlinepolicy.h
+++ b/src/coreclr/jit/inlinepolicy.h
@@ -204,6 +204,8 @@ class ExtendedDefaultPolicy : public DefaultPolicy
, m_FoldableExpr(0)
, m_FoldableExprUn(0)
, m_FoldableBranch(0)
+ , m_FoldableSwitch(0)
+ , m_Switch(0)
, m_DivByCns(0)
, m_ReturnsStructByValue(false)
, m_IsFromValueClass(false)
@@ -252,6 +254,8 @@ class ExtendedDefaultPolicy : public DefaultPolicy
unsigned m_FoldableExpr;
unsigned m_FoldableExprUn;
unsigned m_FoldableBranch;
+ unsigned m_FoldableSwitch;
+ unsigned m_Switch;
unsigned m_DivByCns;
bool m_ReturnsStructByValue : 1;
bool m_IsFromValueClass : 1;
diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp
index 9ce10458fb08e..752626f20184d 100644
--- a/src/coreclr/jit/instr.cpp
+++ b/src/coreclr/jit/instr.cpp
@@ -24,11 +24,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/*****************************************************************************/
#ifdef DEBUG
-/*****************************************************************************
- *
- * Returns the string representation of the given CPU instruction.
- */
-
+//-----------------------------------------------------------------------------
+// genInsName: Returns the string representation of the given CPU instruction, as
+// it exists in the instruction table. Note that some architectures don't encode the
+// name completely in the table: xarch sometimes prepends a "v", and arm sometimes
+// appends a "s". Use `genInsDisplayName()` to get a fully-formed name.
+//
const char* CodeGen::genInsName(instruction ins)
{
// clang-format off
@@ -77,36 +78,36 @@ const char* CodeGen::genInsName(instruction ins)
return insNames[ins];
}
-void __cdecl CodeGen::instDisp(instruction ins, bool noNL, const char* fmt, ...)
+//-----------------------------------------------------------------------------
+// genInsDisplayName: Get a fully-formed instruction display name. This only handles
+// the xarch case of prepending a "v", not the arm case of appending an "s".
+// This can be called up to four times in a single 'printf' before the static buffers
+// get reused.
+//
+// Returns:
+// String with instruction name
+//
+const char* CodeGen::genInsDisplayName(emitter::instrDesc* id)
{
- if (compiler->opts.dspCode)
- {
- /* Display the instruction offset within the emit block */
-
- // printf("[%08X:%04X]", GetEmitter().emitCodeCurBlock(), GetEmitter().emitCodeOffsInBlock());
-
- /* Display the FP stack depth (before the instruction is executed) */
-
- // printf("[FP=%02u] ", genGetFPstkLevel());
+ instruction ins = id->idIns();
+ const char* insName = genInsName(ins);
- /* Display the instruction mnemonic */
- printf(" ");
-
- printf(" %-8s", genInsName(ins));
-
- if (fmt)
- {
- va_list args;
- va_start(args, fmt);
- vprintf(fmt, args);
- va_end(args);
- }
+#ifdef TARGET_XARCH
+ const int TEMP_BUFFER_LEN = 40;
+ static unsigned curBuf = 0;
+ static char buf[4][TEMP_BUFFER_LEN];
+ const char* retbuf;
- if (!noNL)
- {
- printf("\n");
- }
+ if (GetEmitter()->IsAVXInstruction(ins) && !GetEmitter()->IsBMIInstruction(ins))
+ {
+ sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "v%s", insName);
+ retbuf = buf[curBuf];
+ curBuf = (curBuf + 1) % 4;
+ return retbuf;
}
+#endif // TARGET_XARCH
+
+ return insName;
}
/*****************************************************************************/
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 7254e205fbc48..cd685c2cc1032 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -68,6 +68,9 @@ CONFIG_INTEGER(JitAlignLoopAdaptive,
W("JitAlignLoopAdaptive"),
1) // If set, perform adaptive loop alignment that limits number of padding based on loop size.
+// Print the alignment boundaries in disassembly.
+CONFIG_INTEGER(JitDasmWithAlignmentBoundaries, W("JitDasmWithAlignmentBoundaries"), 0)
+
CONFIG_INTEGER(JitDirectAlloc, W("JitDirectAlloc"), 0)
CONFIG_INTEGER(JitDoubleAlign, W("JitDoubleAlign"), 1)
CONFIG_INTEGER(JitDumpASCII, W("JitDumpASCII"), 1) // Uses only ASCII characters in tree dumps
@@ -460,7 +463,8 @@ CONFIG_STRING(JitInlineReplayFile, W("JitInlineReplayFile"))
// Extended version of DefaultPolicy that includes a more precise IL scan,
// relies on PGO if it exists and generally is more aggressive.
CONFIG_INTEGER(JitExtDefaultPolicy, W("JitExtDefaultPolicy"), 1)
-CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x64)
+CONFIG_INTEGER(JitExtDefaultPolicyMaxIL, W("JitExtDefaultPolicyMaxIL"), 0x80)
+CONFIG_INTEGER(JitExtDefaultPolicyMaxILProf, W("JitExtDefaultPolicyMaxILProf"), 0x400)
CONFIG_INTEGER(JitExtDefaultPolicyMaxBB, W("JitExtDefaultPolicyMaxBB"), 7)
// Inliner uses the following formula for PGO-driven decisions:
@@ -552,7 +556,7 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave
#endif // defined(TARGET_ARM64)
#endif // DEBUG
-#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
+#if defined(TARGET_WINDOWS) && defined(TARGET_XARCH)
CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 1) // Allow to enregister locals with struct type.
#else
CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0) // Don't allow to enregister locals with struct type
diff --git a/src/coreclr/jit/jitstd/algorithm.h b/src/coreclr/jit/jitstd/algorithm.h
index 000639a5a1de8..9fa6fbb94dd54 100644
--- a/src/coreclr/jit/jitstd/algorithm.h
+++ b/src/coreclr/jit/jitstd/algorithm.h
@@ -102,9 +102,9 @@ void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Less less
//
// It's not possible for newFirst to go past the end of the sort range:
// - If newFirst reaches the pivot before newLast then the pivot is
- // swapped to the right and we'll stop again when we reach it.
+ // swapped to the right and we'll stop again when we reach it.
// - If newLast reaches the pivot before newFirst then the pivot is
- // swapped to the left and the value at newFirst will take its place
+ // swapped to the left and the value at newFirst will take its place
// to the right so less(newFirst, pivot) will again be false when the
// old pivot's position is reached.
do
diff --git a/src/coreclr/jit/jitstd/list.h b/src/coreclr/jit/jitstd/list.h
index 070d94361f2aa..b573a3952fb17 100644
--- a/src/coreclr/jit/jitstd/list.h
+++ b/src/coreclr/jit/jitstd/list.h
@@ -160,6 +160,17 @@ class list
Node* m_pNode;
};
+#ifdef DEBUG
+ void init(const Allocator& a)
+ {
+ m_pHead = nullptr;
+ m_pTail = nullptr;
+ m_nSize = 0;
+ m_allocator = a;
+ m_nodeAllocator = a;
+ }
+#endif
+
explicit list(const Allocator&);
list(size_type n, const T& value, const Allocator&);
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 33c5abaa30df7..75bcbfafbf290 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -3497,6 +3497,10 @@ void Compiler::lvaSortByRefCount()
{
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
}
+ else if (!varDsc->IsEnregisterableType())
+ {
+ lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
+ }
}
if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
{
diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp
index 0fe22420f783e..c3991e663e1d9 100644
--- a/src/coreclr/jit/loopcloning.cpp
+++ b/src/coreclr/jit/loopcloning.cpp
@@ -12,6 +12,40 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "jitpch.h"
+#ifdef DEBUG
+
+//--------------------------------------------------------------------------------------------------
+// ArrIndex::Print - debug print an ArrIndex struct in form: `V01[V02][V03]`.
+//
+// Arguments:
+// dim (Optional) Print up to but not including this dimension. Default: print all dimensions.
+//
+void ArrIndex::Print(unsigned dim /* = -1 */)
+{
+ printf("V%02d", arrLcl);
+ for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i)
+ {
+ printf("[V%02d]", indLcls.Get(i));
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+// ArrIndex::PrintBoundsCheckNodes - debug print an ArrIndex struct bounds check node tree ids in
+// form: `[000125][000113]`.
+//
+// Arguments:
+// dim (Optional) Print up to but not including this dimension. Default: print all dimensions.
+//
+void ArrIndex::PrintBoundsCheckNodes(unsigned dim /* = -1 */)
+{
+ for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i)
+ {
+ Compiler::printTreeID(bndsChks.Get(i));
+ }
+}
+
+#endif // DEBUG
+
//--------------------------------------------------------------------------------------------------
// ToGenTree - Convert an arrLen operation into a gentree node.
//
@@ -39,11 +73,26 @@ GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
{
arr = comp->gtNewIndexRef(TYP_REF, arr, comp->gtNewLclvNode(arrIndex->indLcls[i],
comp->lvaTable[arrIndex->indLcls[i]].lvType));
+
+ // Clear the range check flag and mark the index as non-faulting: we guarantee that all necessary range
+ // checking has already been done by the time this array index expression is invoked.
+ arr->gtFlags &= ~(GTF_INX_RNGCHK | GTF_EXCEPT);
+ arr->gtFlags |= GTF_INX_NOFAULT;
}
// If asked for arrlen invoke arr length operator.
if (oper == ArrLen)
{
GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb);
+
+ // We already guaranteed (by a sequence of preceding checks) that the array length operator will not
+ // throw an exception because we null checked the base array.
+ // So, we should be able to do the following:
+ // arrLen->gtFlags &= ~GTF_EXCEPT;
+ // arrLen->gtFlags |= GTF_IND_NONFAULTING;
+ // However, we then end up with a mix of non-faulting array length operators as well as normal faulting
+ // array length operators in the slow-path of the cloned loops. CSE doesn't keep these separate, so bails
+ // out on creating CSEs on this very useful type of CSE, leading to CQ losses in the cloned loop fast path.
+ // TODO-CQ: fix this.
return arrLen;
}
else
@@ -395,26 +444,30 @@ void LoopCloneContext::PrintBlockConditions(unsigned loopNum)
{
printf("Block conditions:\n");
- JitExpandArrayStack*>* levelCond = blockConditions[loopNum];
- if (levelCond == nullptr || levelCond->Size() == 0)
+ JitExpandArrayStack*>* blockConds = blockConditions[loopNum];
+ if (blockConds == nullptr || blockConds->Size() == 0)
{
printf("No block conditions\n");
return;
}
- for (unsigned i = 0; i < levelCond->Size(); ++i)
+ for (unsigned i = 0; i < blockConds->Size(); ++i)
+ {
+ PrintBlockLevelConditions(i, (*blockConds)[i]);
+ }
+}
+void LoopCloneContext::PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond)
+{
+ printf("%d = ", level);
+ for (unsigned j = 0; j < levelCond->Size(); ++j)
{
- printf("%d = {", i);
- for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j)
+ if (j != 0)
{
- if (j != 0)
- {
- printf(" & ");
- }
- (*((*levelCond)[i]))[j].Print();
+ printf(" & ");
}
- printf("}\n");
+ (*levelCond)[j].Print();
}
+ printf("\n");
}
#endif
@@ -650,19 +703,19 @@ void LoopCloneContext::PrintConditions(unsigned loopNum)
{
if (conditions[loopNum] == nullptr)
{
- JITDUMP("NO conditions");
+ printf("NO conditions");
return;
}
if (conditions[loopNum]->Size() == 0)
{
- JITDUMP("Conditions were optimized away! Will always take cloned path.");
+ printf("Conditions were optimized away! Will always take cloned path.");
return;
}
for (unsigned i = 0; i < conditions[loopNum]->Size(); ++i)
{
if (i != 0)
{
- JITDUMP(" & ");
+ printf(" & ");
}
(*conditions[loopNum])[i].Print();
}
@@ -711,6 +764,9 @@ void LoopCloneContext::CondToStmtInBlock(Compiler* comp
comp->fgInsertStmtAtEnd(block, stmt);
// Remorph.
+ JITDUMP("Loop cloning condition tree before morphing:\n");
+ DBEXEC(comp->verbose, comp->gtDispTree(jmpTrueTree));
+ JITDUMP("\n");
comp->fgMorphBlockStmt(block, stmt DEBUGARG("Loop cloning condition"));
}
@@ -965,16 +1021,15 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext
for (unsigned i = 0; i < optInfos->Size(); ++i)
{
- LcOptInfo* optInfo = optInfos->GetRef(i);
+ LcOptInfo* optInfo = optInfos->Get(i);
switch (optInfo->GetOptType())
{
case LcOptInfo::LcJaggedArray:
{
// limit <= arrLen
LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo();
- LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen);
- LC_Ident arrLenIdent = LC_Ident(arrLen);
-
+ LC_Array arrLen(LC_Array::Jagged, &arrIndexInfo->arrIndex, arrIndexInfo->dim, LC_Array::ArrLen);
+ LC_Ident arrLenIdent = LC_Ident(arrLen);
LC_Condition cond(GT_LE, LC_Expr(ident), LC_Expr(arrLenIdent));
context->EnsureConditions(loopNum)->Push(cond);
@@ -1000,9 +1055,9 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext
return false;
}
}
- JITDUMP("Conditions: (");
+ JITDUMP("Conditions: ");
DBEXEC(verbose, context->PrintConditions(loopNum));
- JITDUMP(")\n");
+ JITDUMP("\n");
return true;
}
return false;
@@ -1164,10 +1219,6 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con
printf("Deref condition tree:\n");
for (unsigned i = 0; i < nodes.Size(); ++i)
{
- if (i != 0)
- {
- printf(",");
- }
nodes[i]->Print();
printf("\n");
}
@@ -1176,6 +1227,7 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con
if (maxRank == -1)
{
+ JITDUMP("> maxRank undefined\n");
return false;
}
@@ -1184,12 +1236,13 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con
// So add 1 after rank * 2.
unsigned condBlocks = (unsigned)maxRank * 2 + 1;
- // Heuristic to not create too many blocks.
- // REVIEW: due to the definition of `condBlocks`, above, the effective max is 3 blocks, meaning
- // `maxRank` of 1. Question: should the heuristic allow more blocks to be created in some situations?
- // REVIEW: make this based on a COMPlus configuration?
- if (condBlocks > 4)
+ // Heuristic to not create too many blocks. Defining as 3 allows, effectively, loop cloning on
+ // doubly-nested loops.
+ // REVIEW: make this based on a COMPlus configuration, at least for debug?
+ const unsigned maxAllowedCondBlocks = 3;
+ if (condBlocks > maxAllowedCondBlocks)
{
+ JITDUMP("> Too many condition blocks (%u > %u)\n", condBlocks, maxAllowedCondBlocks);
return false;
}
@@ -1254,14 +1307,60 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext*
JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum);
for (unsigned i = 0; i < optInfos->Size(); ++i)
{
- LcOptInfo* optInfo = optInfos->GetRef(i);
+ LcOptInfo* optInfo = optInfos->Get(i);
switch (optInfo->GetOptType())
{
case LcOptInfo::LcJaggedArray:
{
LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo();
compCurBB = arrIndexInfo->arrIndex.useBlock;
- optRemoveCommaBasedRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt);
+
+ // Remove all bounds checks for this array up to (and including) `arrIndexInfo->dim`. So, if that is 1,
+ // Remove rank 0 and 1 bounds checks.
+
+ for (unsigned dim = 0; dim <= arrIndexInfo->dim; dim++)
+ {
+ GenTree* bndsChkNode = arrIndexInfo->arrIndex.bndsChks[dim];
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("Remove bounds check ");
+ printTreeID(bndsChkNode->gtGetOp1());
+ printf(" for " FMT_STMT ", dim% d, ", arrIndexInfo->stmt->GetID(), dim);
+ arrIndexInfo->arrIndex.Print();
+ printf(", bounds check nodes: ");
+ arrIndexInfo->arrIndex.PrintBoundsCheckNodes();
+ printf("\n");
+ }
+#endif // DEBUG
+
+ if (bndsChkNode->gtGetOp1()->OperIsBoundsCheck())
+ {
+ // 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:
+ // for (i = 0; i < x; i++)
+ // for (j = 0; j < y; j++)
+ // a[i][j] = i + j;
+ // If the outer loop is cloned first, it will remove the a[i] bounds check from the optimized
+ // path. Later, when the inner loop is cloned, we want to remove the a[i][j] bounds check. If
+ // we clone the inner loop, we know that the a[i] bounds check isn't required because we'll add
+ // it to the loop cloning conditions. On the other hand, we can clone a loop where we get rid of
+ // the nested bounds check but nobody has gotten rid of the outer bounds check. As before, we
+ // know the outer bounds check is not needed because it's been added to the cloning conditions,
+ // so we can get rid of the bounds check here.
+ //
+ optRemoveCommaBasedRangeCheck(bndsChkNode, arrIndexInfo->stmt);
+ }
+ else
+ {
+ JITDUMP(" Bounds check already removed\n");
+
+ // If the bounds check node isn't there, it better have been converted to a GT_NOP.
+ assert(bndsChkNode->gtGetOp1()->OperIs(GT_NOP));
+ }
+ }
+
DBEXEC(dynamicPath, optDebugLogLoopCloning(arrIndexInfo->arrIndex.useBlock, arrIndexInfo->stmt));
}
break;
@@ -1474,8 +1573,9 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context,
BasicBlock* head,
BasicBlock* slowHead)
{
- JITDUMP("Inserting loop cloning conditions\n");
+ JITDUMP("Inserting loop " FMT_LP " loop choice conditions\n", loopNum);
assert(context->HasBlockConditions(loopNum));
+ assert(head->bbJumpKind == BBJ_COND);
BasicBlock* curCond = head;
JitExpandArrayStack*>* levelCond = context->GetBlockConditions(loopNum);
@@ -1484,10 +1584,15 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context,
bool isHeaderBlock = (curCond == head);
// Flip the condition if header block.
+ JITDUMP("Adding loop " FMT_LP " level %u block conditions to " FMT_BB "\n ", loopNum, i, curCond->bbNum);
+ DBEXEC(verbose, context->PrintBlockLevelConditions(i, (*levelCond)[i]));
context->CondToStmtInBlock(this, *((*levelCond)[i]), curCond, /*reverse*/ isHeaderBlock);
// Create each condition block ensuring wiring between them.
- BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true);
+ BasicBlock* tmp = fgNewBBafter(BBJ_COND, isHeaderBlock ? slowHead : curCond, /*extendRegion*/ true);
+ tmp->inheritWeight(head);
+ tmp->bbNatLoopNum = head->bbNatLoopNum;
+
curCond->bbJumpDest = isHeaderBlock ? tmp : slowHead;
JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", curCond->bbNum, curCond->bbJumpDest->bbNum);
@@ -1500,13 +1605,13 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context,
}
curCond = tmp;
-
- curCond->inheritWeight(head);
- curCond->bbNatLoopNum = head->bbNatLoopNum;
- JITDUMP("Created new " FMT_BB " for new level %u\n", curCond->bbNum, i);
}
// Finally insert cloning conditions after all deref conditions have been inserted.
+ JITDUMP("Adding loop " FMT_LP " cloning conditions to " FMT_BB "\n", loopNum, curCond->bbNum);
+ JITDUMP(" ");
+ DBEXEC(verbose, context->PrintConditions(loopNum));
+ JITDUMP("\n");
context->CondToStmtInBlock(this, *(context->GetConditions(loopNum)), curCond, /*reverse*/ false);
return curCond;
}
@@ -2222,10 +2327,12 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
#ifdef DEBUG
if (verbose)
{
- printf("Found ArrIndex at tree ");
+ printf("Found ArrIndex at " FMT_BB " " FMT_STMT " tree ", arrIndex.useBlock->bbNum, info->stmt->GetID());
printTreeID(tree);
printf(" which is equivalent to: ");
arrIndex.Print();
+ printf(", bounds check nodes: ");
+ arrIndex.PrintBoundsCheckNodes();
printf("\n");
}
#endif
@@ -2233,6 +2340,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
// Check that the array object local variable is invariant within the loop body.
if (!optIsStackLocalInvariant(info->loopNum, arrIndex.arrLcl))
{
+ JITDUMP("V%02d is not loop invariant\n", arrIndex.arrLcl);
return WALK_SKIP_SUBTREES;
}
diff --git a/src/coreclr/jit/loopcloning.h b/src/coreclr/jit/loopcloning.h
index 6cc921c520db1..c5ed53c31bad8 100644
--- a/src/coreclr/jit/loopcloning.h
+++ b/src/coreclr/jit/loopcloning.h
@@ -220,14 +220,8 @@ struct ArrIndex
}
#ifdef DEBUG
- void Print(unsigned dim = -1)
- {
- printf("V%02d", arrLcl);
- for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i)
- {
- printf("[V%02d]", indLcls.GetRef(i));
- }
- }
+ void Print(unsigned dim = -1);
+ void PrintBoundsCheckNodes(unsigned dim = -1);
#endif
};
@@ -260,9 +254,8 @@ struct LcOptInfo
#include "loopcloningopts.h"
};
- void* optInfo;
OptType optType;
- LcOptInfo(void* optInfo, OptType optType) : optInfo(optInfo), optType(optType)
+ LcOptInfo(OptType optType) : optType(optType)
{
}
@@ -270,6 +263,7 @@ struct LcOptInfo
{
return optType;
}
+
#define LC_OPT(en) \
en##OptInfo* As##en##OptInfo() \
{ \
@@ -292,7 +286,7 @@ struct LcMdArrayOptInfo : public LcOptInfo
ArrIndex* index; // "index" cached computation in the form of an ArrIndex representation.
LcMdArrayOptInfo(GenTreeArrElem* arrElem, unsigned dim)
- : LcOptInfo(this, LcMdArray), arrElem(arrElem), dim(dim), index(nullptr)
+ : LcOptInfo(LcMdArray), arrElem(arrElem), dim(dim), index(nullptr)
{
}
@@ -325,7 +319,7 @@ struct LcJaggedArrayOptInfo : public LcOptInfo
Statement* stmt; // "stmt" where the optimization opportunity occurs.
LcJaggedArrayOptInfo(ArrIndex& arrIndex, unsigned dim, Statement* stmt)
- : LcOptInfo(this, LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt)
+ : LcOptInfo(LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt)
{
}
};
@@ -678,30 +672,24 @@ struct LoopCloneContext
CompAllocator alloc; // The allocator
// The array of optimization opportunities found in each loop. (loop x optimization-opportunities)
- JitExpandArrayStack** optInfo;
+ jitstd::vector*> optInfo;
// The array of conditions that influence which path to take for each loop. (loop x cloning-conditions)
- JitExpandArrayStack** conditions;
+ jitstd::vector*> conditions;
// The array of dereference conditions found in each loop. (loop x deref-conditions)
- JitExpandArrayStack** derefs;
+ jitstd::vector*> derefs;
// The array of block levels of conditions for each loop. (loop x level x conditions)
- JitExpandArrayStack*>** blockConditions;
+ jitstd::vector*>*> blockConditions;
- LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc)
+ LoopCloneContext(unsigned loopCount, CompAllocator alloc)
+ : alloc(alloc), optInfo(alloc), conditions(alloc), derefs(alloc), blockConditions(alloc)
{
- optInfo = new (alloc) JitExpandArrayStack*[loopCount];
- conditions = new (alloc) JitExpandArrayStack*[loopCount];
- derefs = new (alloc) JitExpandArrayStack*[loopCount];
- blockConditions = new (alloc) JitExpandArrayStack*>*[loopCount];
- for (unsigned i = 0; i < loopCount; ++i)
- {
- optInfo[i] = nullptr;
- conditions[i] = nullptr;
- derefs[i] = nullptr;
- blockConditions[i] = nullptr;
- }
+ optInfo.resize(loopCount, nullptr);
+ conditions.resize(loopCount, nullptr);
+ derefs.resize(loopCount, nullptr);
+ blockConditions.resize(loopCount, nullptr);
}
// Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true.
@@ -739,6 +727,7 @@ struct LoopCloneContext
#ifdef DEBUG
// Print the block conditions for the loop.
void PrintBlockConditions(unsigned loopNum);
+ void PrintBlockLevelConditions(unsigned level, JitExpandArrayStack* levelCond);
#endif
// Does the loop have block conditions?
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 9499ac5d81782..a5b0ba16fdc6c 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -116,7 +116,7 @@ GenTree* Lowering::LowerNode(GenTree* node)
break;
case GT_STOREIND:
- LowerStoreIndirCommon(node->AsIndir());
+ LowerStoreIndirCommon(node->AsStoreInd());
break;
case GT_ADD:
@@ -3558,7 +3558,7 @@ void Lowering::LowerStoreSingleRegCallStruct(GenTreeBlk* store)
{
store->ChangeType(regType);
store->SetOper(GT_STOREIND);
- LowerStoreIndirCommon(store);
+ LowerStoreIndirCommon(store->AsStoreInd());
return;
}
else
@@ -4100,7 +4100,7 @@ void Lowering::InsertPInvokeMethodProlog()
// The init routine sets InlinedCallFrame's m_pNext, so we just set the thead's top-of-stack
GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame);
firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd));
- ContainCheckStoreIndir(frameUpd->AsIndir());
+ ContainCheckStoreIndir(frameUpd->AsStoreInd());
DISPTREERANGE(firstBlockRange, frameUpd);
}
#endif // TARGET_64BIT
@@ -4163,7 +4163,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree*
{
GenTree* frameUpd = CreateFrameLinkUpdate(PopFrame);
returnBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd));
- ContainCheckStoreIndir(frameUpd->AsIndir());
+ ContainCheckStoreIndir(frameUpd->AsStoreInd());
}
}
@@ -4325,7 +4325,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
// Stubs do this once per stub, not once per call.
GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, frameUpd));
- ContainCheckStoreIndir(frameUpd->AsIndir());
+ ContainCheckStoreIndir(frameUpd->AsStoreInd());
}
#endif // TARGET_64BIT
@@ -4335,7 +4335,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
// [tcb + offsetOfGcState] = 0
GenTree* storeGCState = SetGCState(0);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, storeGCState));
- ContainCheckStoreIndir(storeGCState->AsIndir());
+ ContainCheckStoreIndir(storeGCState->AsStoreInd());
// Indicate that codegen has switched this thread to preemptive GC.
// This tree node doesn't generate any code, but impacts LSRA and gc reporting.
@@ -4381,7 +4381,7 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
GenTree* tree = SetGCState(1);
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
- ContainCheckStoreIndir(tree->AsIndir());
+ ContainCheckStoreIndir(tree->AsStoreInd());
tree = CreateReturnTrapSeq();
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
@@ -4396,7 +4396,7 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
{
tree = CreateFrameLinkUpdate(PopFrame);
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
- ContainCheckStoreIndir(tree->AsIndir());
+ ContainCheckStoreIndir(tree->AsStoreInd());
}
#else
const CORINFO_EE_INFO::InlinedCallFrameInfo& callFrameInfo = comp->eeGetEEInfo()->inlinedCallFrameInfo;
@@ -6421,7 +6421,7 @@ void Lowering::ContainCheckNode(GenTree* node)
ContainCheckReturnTrap(node->AsOp());
break;
case GT_STOREIND:
- ContainCheckStoreIndir(node->AsIndir());
+ ContainCheckStoreIndir(node->AsStoreInd());
break;
case GT_IND:
ContainCheckIndir(node->AsIndir());
@@ -6604,9 +6604,8 @@ void Lowering::ContainCheckBitCast(GenTree* node)
// Arguments:
// ind - the store indirection node we are lowering.
//
-void Lowering::LowerStoreIndirCommon(GenTreeIndir* ind)
+void Lowering::LowerStoreIndirCommon(GenTreeStoreInd* ind)
{
- assert(ind->OperIs(GT_STOREIND));
assert(ind->TypeGet() != TYP_STRUCT);
TryCreateAddrMode(ind->Addr(), true);
if (!comp->codeGen->gcInfo.gcIsWriteBarrierStoreIndNode(ind))
@@ -6806,6 +6805,6 @@ bool Lowering::TryTransformStoreObjAsStoreInd(GenTreeBlk* blkNode)
{
assert(src->TypeIs(regType) || src->IsCnsIntOrI() || src->IsCall());
}
- LowerStoreIndirCommon(blkNode);
+ LowerStoreIndirCommon(blkNode->AsStoreInd());
return true;
}
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index 7f7c9d3760aa1..a0d897e74da16 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -89,7 +89,7 @@ class Lowering final : public Phase
void ContainCheckBitCast(GenTree* node);
void ContainCheckCallOperands(GenTreeCall* call);
void ContainCheckIndir(GenTreeIndir* indirNode);
- void ContainCheckStoreIndir(GenTreeIndir* indirNode);
+ void ContainCheckStoreIndir(GenTreeStoreInd* indirNode);
void ContainCheckMul(GenTreeOp* node);
void ContainCheckShiftRotate(GenTreeOp* node);
void ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const;
@@ -292,9 +292,9 @@ class Lowering final : public Phase
#endif // defined(TARGET_XARCH)
// Per tree node member functions
- void LowerStoreIndirCommon(GenTreeIndir* ind);
+ void LowerStoreIndirCommon(GenTreeStoreInd* ind);
void LowerIndir(GenTreeIndir* ind);
- void LowerStoreIndir(GenTreeIndir* node);
+ void LowerStoreIndir(GenTreeStoreInd* node);
GenTree* LowerAdd(GenTreeOp* node);
bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
GenTree* LowerConstIntDivOrMod(GenTree* node);
diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp
index 7edf8c7103f15..134b77281f681 100644
--- a/src/coreclr/jit/lowerarmarch.cpp
+++ b/src/coreclr/jit/lowerarmarch.cpp
@@ -218,7 +218,7 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
// Return Value:
// None.
//
-void Lowering::LowerStoreIndir(GenTreeIndir* node)
+void Lowering::LowerStoreIndir(GenTreeStoreInd* node)
{
ContainCheckStoreIndir(node);
}
@@ -1376,11 +1376,11 @@ void Lowering::ContainCheckCallOperands(GenTreeCall* call)
// Arguments:
// node - pointer to the node
//
-void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
+void Lowering::ContainCheckStoreIndir(GenTreeStoreInd* node)
{
#ifdef TARGET_ARM64
- GenTree* src = node->AsOp()->gtOp2;
- if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
+ GenTree* src = node->Data();
+ if (src->IsIntegralConst(0))
{
// an integer zero for 'src' can be contained.
MakeSrcContained(node, src);
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index ed889f7f383bc..43c0df6204236 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -110,11 +110,11 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
// Return Value:
// None.
//
-void Lowering::LowerStoreIndir(GenTreeIndir* node)
+void Lowering::LowerStoreIndir(GenTreeStoreInd* node)
{
// Mark all GT_STOREIND nodes to indicate that it is not known
// whether it represents a RMW memory op.
- node->AsStoreInd()->SetRMWStatusDefault();
+ node->SetRMWStatusDefault();
if (!varTypeIsFloating(node))
{
@@ -130,10 +130,10 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node)
return;
}
}
- else if (node->AsStoreInd()->Data()->OperIs(GT_CNS_DBL))
+ else if (node->Data()->IsCnsFltOrDbl())
{
// Optimize *x = DCON to *x = ICON which is slightly faster on xarch
- GenTree* data = node->AsStoreInd()->Data();
+ GenTree* data = node->Data();
double dblCns = data->AsDblCon()->gtDconVal;
ssize_t intCns = 0;
var_types type = TYP_UNKNOWN;
@@ -162,6 +162,13 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node)
node->ChangeType(type);
}
}
+
+ // Optimization: do not unnecessarily zero-extend the result of setcc.
+ if (varTypeIsByte(node) && (node->Data()->OperIsCompare() || node->Data()->OperIs(GT_SETCC)))
+ {
+ node->Data()->ChangeType(TYP_BYTE);
+ }
+
ContainCheckStoreIndir(node);
}
@@ -4588,17 +4595,18 @@ void Lowering::ContainCheckIndir(GenTreeIndir* node)
// Arguments:
// node - pointer to the node
//
-void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
+void Lowering::ContainCheckStoreIndir(GenTreeStoreInd* node)
{
// If the source is a containable immediate, make it contained, unless it is
// an int-size or larger store of zero to memory, because we can generate smaller code
// by zeroing a register and then storing it.
- GenTree* src = node->AsOp()->gtOp2;
+ GenTree* src = node->Data();
if (IsContainableImmed(node, src) &&
- (!src->IsIntegralConst(0) || varTypeIsSmall(node) || node->gtGetOp1()->OperGet() == GT_CLS_VAR_ADDR))
+ (!src->IsIntegralConst(0) || varTypeIsSmall(node) || node->Addr()->OperIs(GT_CLS_VAR_ADDR)))
{
MakeSrcContained(node, src);
}
+
ContainCheckIndir(node);
}
diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index 4bbb877fda8c3..f382b1dcf4634 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -6180,10 +6180,21 @@ void LinearScan::insertCopyOrReload(BasicBlock* block, GenTree* tree, unsigned m
}
else
{
- // Create the new node, with "tree" as its only child.
- var_types treeType = tree->TypeGet();
+ var_types regType = tree->TypeGet();
+ if ((regType == TYP_STRUCT) && !tree->IsMultiRegNode())
+ {
+ assert(compiler->compEnregStructLocals());
+ assert(tree->IsLocal());
+ const GenTreeLclVarCommon* lcl = tree->AsLclVarCommon();
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(lcl);
+ // We create struct copies with a primitive type so we don't bother copy node with parsing structHndl.
+ // Note that for multiReg node we keep each regType in the tree and don't need this.
+ regType = varDsc->GetRegisterType(lcl);
+ assert(regType != TYP_UNDEF);
+ }
- GenTreeCopyOrReload* newNode = new (compiler, oper) GenTreeCopyOrReload(oper, treeType, tree);
+ // Create the new node, with "tree" as its only child.
+ GenTreeCopyOrReload* newNode = new (compiler, oper) GenTreeCopyOrReload(oper, regType, tree);
assert(refPosition->registerAssignment != RBM_NONE);
SetLsraAdded(newNode);
newNode->SetRegNumByIdx(refPosition->assignedReg(), multiRegIdx);
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index f5f4d781d759e..da7cb15b7cd08 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -3559,6 +3559,7 @@ int LinearScan::BuildReturn(GenTree* tree)
noway_assert(op1->IsMultiRegCall() || op1->IsMultiRegLclVar());
int srcCount;
+ ReturnTypeDesc nonCallRetTypeDesc;
const ReturnTypeDesc* pRetTypeDesc;
if (op1->OperIs(GT_CALL))
{
@@ -3567,13 +3568,12 @@ int LinearScan::BuildReturn(GenTree* tree)
else
{
assert(compiler->lvaEnregMultiRegVars);
- LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum());
- ReturnTypeDesc retTypeDesc;
- retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(),
- compiler->info.compCallConv);
- pRetTypeDesc = &retTypeDesc;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum());
+ nonCallRetTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(),
+ compiler->info.compCallConv);
+ pRetTypeDesc = &nonCallRetTypeDesc;
assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt ==
- retTypeDesc.GetReturnRegCount());
+ nonCallRetTypeDesc.GetReturnRegCount());
}
srcCount = pRetTypeDesc->GetReturnRegCount();
// For any source that's coming from a different register file, we need to ensure that
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 526a996e1b40c..354b1e4a35749 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -5581,8 +5581,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
GenTree* arrRef = asIndex->Arr();
GenTree* index = asIndex->Index();
- bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled
- bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0);
+ bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled
+ bool indexNonFaulting = ((tree->gtFlags & GTF_INX_NOFAULT) != 0); // if true, mark GTF_IND_NONFAULTING
+ bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0);
GenTree* arrRefDefn = nullptr; // non-NULL if we need to allocate a temp for the arrRef expression
GenTree* indexDefn = nullptr; // non-NULL if we need to allocate a temp for the index expression
@@ -5742,9 +5743,9 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
this->compFloatingPointUsed = true;
}
- // We've now consumed the GTF_INX_RNGCHK, and the node
+ // We've now consumed the GTF_INX_RNGCHK and GTF_INX_NOFAULT, and the node
// is no longer a GT_INDEX node.
- tree->gtFlags &= ~GTF_INX_RNGCHK;
+ tree->gtFlags &= ~(GTF_INX_RNGCHK | GTF_INX_NOFAULT);
tree->AsOp()->gtOp1 = addr;
@@ -5752,7 +5753,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
tree->gtFlags |= GTF_IND_ARR_INDEX;
// If there's a bounds check, the indir won't fault.
- if (bndsChk)
+ if (bndsChk || indexNonFaulting)
{
tree->gtFlags |= GTF_IND_NONFAULTING;
}
@@ -12070,10 +12071,11 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
tree->AsOp()->gtOp1 = op1;
}
- /* If we are storing a small type, we might be able to omit a cast */
- if ((effectiveOp1->gtOper == GT_IND) && varTypeIsSmall(effectiveOp1->TypeGet()))
+ // If we are storing a small type, we might be able to omit a cast.
+ if (effectiveOp1->OperIs(GT_IND) && varTypeIsSmall(effectiveOp1))
{
- if (!gtIsActiveCSE_Candidate(op2) && (op2->gtOper == GT_CAST) && !op2->gtOverflow())
+ if (!gtIsActiveCSE_Candidate(op2) && op2->OperIs(GT_CAST) &&
+ varTypeIsIntegral(op2->AsCast()->CastOp()) && !op2->gtOverflow())
{
var_types castType = op2->CastToType();
@@ -12081,28 +12083,13 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
// castType is larger or the same as op1's type
// then we can discard the cast.
- if (varTypeIsSmall(castType) && (genTypeSize(castType) >= genTypeSize(effectiveOp1->TypeGet())))
+ if (varTypeIsSmall(castType) && (genTypeSize(castType) >= genTypeSize(effectiveOp1)))
{
tree->AsOp()->gtOp2 = op2 = op2->AsCast()->CastOp();
}
}
- else if (op2->OperIsCompare() && varTypeIsByte(effectiveOp1->TypeGet()))
- {
- /* We don't need to zero extend the setcc instruction */
- op2->gtType = TYP_BYTE;
- }
}
- // If we introduced a CSE we may need to undo the optimization above
- // (i.e. " op2->gtType = TYP_BYTE;" which depends upon op1 being a GT_IND of a byte type)
- // When we introduce the CSE we remove the GT_IND and subsitute a GT_LCL_VAR in it place.
- else if (op2->OperIsCompare() && (op2->gtType == TYP_BYTE) && (op1->gtOper == GT_LCL_VAR))
- {
- unsigned varNum = op1->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[varNum];
- /* We again need to zero extend the setcc instruction */
- op2->gtType = varDsc->TypeGet();
- }
fgAssignSetVarDef(tree);
/* We can't CSE the LHS of an assignment */
@@ -12344,9 +12331,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
gtReverseCond(op1);
}
- /* Propagate gtType of tree into op1 in case it is TYP_BYTE for setcc optimization */
- op1->gtType = tree->gtType;
-
noway_assert((op1->gtFlags & GTF_RELOP_JMP_USED) == 0);
op1->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
@@ -12481,44 +12465,54 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
noway_assert(op1->TypeGet() == TYP_LONG && op1->OperGet() == GT_AND);
- /* Is the result of the mask effectively an INT ? */
-
- GenTree* andMask;
- andMask = op1->AsOp()->gtOp2;
- if (andMask->gtOper != GT_CNS_NATIVELONG)
- {
- goto COMPARE;
- }
- if ((andMask->AsIntConCommon()->LngValue() >> 32) != 0)
+ // The transform below cannot preserve VNs.
+ if (fgGlobalMorph)
{
- goto COMPARE;
- }
+ // Is the result of the mask effectively an INT ?
+
+ GenTree* andMask = op1->AsOp()->gtOp2;
- /* Now we know that we can cast AsOp()->gtOp1 of AND to int */
+ if (andMask->gtOper != GT_CNS_NATIVELONG)
+ {
+ goto COMPARE;
+ }
+ if ((andMask->AsIntConCommon()->LngValue() >> 32) != 0)
+ {
+ goto COMPARE;
+ }
- op1->AsOp()->gtOp1 = gtNewCastNode(TYP_INT, op1->AsOp()->gtOp1, false, TYP_INT);
+ // Now we narrow AsOp()->gtOp1 of AND to int.
+ if (optNarrowTree(op1->AsOp()->gtGetOp1(), TYP_LONG, TYP_INT, ValueNumPair(), false))
+ {
+ optNarrowTree(op1->AsOp()->gtGetOp1(), TYP_LONG, TYP_INT, ValueNumPair(), true);
+ }
+ else
+ {
+ op1->AsOp()->gtOp1 = gtNewCastNode(TYP_INT, op1->AsOp()->gtGetOp1(), false, TYP_INT);
+ }
- /* now replace the mask node (AsOp()->gtOp2 of AND node) */
+ // now replace the mask node (AsOp()->gtOp2 of AND node).
- noway_assert(andMask == op1->AsOp()->gtOp2);
+ noway_assert(andMask == op1->AsOp()->gtOp2);
- ival1 = (int)andMask->AsIntConCommon()->LngValue();
- andMask->SetOper(GT_CNS_INT);
- andMask->gtType = TYP_INT;
- andMask->AsIntCon()->gtIconVal = ival1;
+ ival1 = (int)andMask->AsIntConCommon()->LngValue();
+ andMask->SetOper(GT_CNS_INT);
+ andMask->gtType = TYP_INT;
+ andMask->AsIntCon()->gtIconVal = ival1;
- /* now change the type of the AND node */
+ // now change the type of the AND node.
- op1->gtType = TYP_INT;
+ op1->gtType = TYP_INT;
- /* finally we replace the comparand */
+ // finally we replace the comparand.
- ival2 = (int)cns2->AsIntConCommon()->LngValue();
- cns2->SetOper(GT_CNS_INT);
- cns2->gtType = TYP_INT;
+ ival2 = (int)cns2->AsIntConCommon()->LngValue();
+ cns2->SetOper(GT_CNS_INT);
+ cns2->gtType = TYP_INT;
- noway_assert(cns2 == op2);
- cns2->AsIntCon()->gtIconVal = ival2;
+ noway_assert(cns2 == op2);
+ cns2->AsIntCon()->gtIconVal = ival2;
+ }
goto COMPARE;
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index b4137ec52ebbf..587349e489b55 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -166,7 +166,8 @@ Compiler::fgWalkResult Compiler::optCSE_MaskHelper(GenTree** pTree, fgWalkData*
if (IS_CSE_INDEX(tree->gtCSEnum))
{
unsigned cseIndex = GET_CSE_INDEX(tree->gtCSEnum);
- unsigned cseBit = genCSEnum2bit(cseIndex);
+ // Note that we DO NOT use getCSEAvailBit() here, for the CSE_defMask/CSE_useMask
+ unsigned cseBit = genCSEnum2bit(cseIndex);
if (IS_CSE_DEF(tree->gtCSEnum))
{
BitVecOps::AddElemD(comp->cseMaskTraits, pUserData->CSE_defMask, cseBit);
@@ -424,16 +425,16 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
ValueNum vnLibNorm = vnStore->VNNormalValue(vnLib);
// We use the normal value number because we want the CSE candidate to
- // represent all expressions that produce the same normal value number
+ // represent all expressions that produce the same normal value number.
// We will handle the case where we have different exception sets when
// promoting the candidates.
//
// We do this because a GT_IND will usually have a NullPtrExc entry in its
// exc set, but we may have cleared the GTF_EXCEPT flag and if so, it won't
- // have an NullPtrExc, or we may have assigned the value of an GT_IND
+ // have an NullPtrExc, or we may have assigned the value of an GT_IND
// into a LCL_VAR and then read it back later.
//
- // When we are promoting the CSE candidates we insure that any CSE
+ // When we are promoting the CSE candidates we ensure that any CSE
// uses that we promote have an exc set that is the same as the CSE defs
// or have an empty set. And that all of the CSE defs produced the required
// set of exceptions for the CSE uses.
@@ -502,7 +503,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
key = vnLibNorm;
}
- // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst
+ // Make sure that the result of Is_Shared_Const_CSE(key) matches isSharedConst.
// Note that when isSharedConst is true then we require that the TARGET_SIGN_BIT is set in the key
// and otherwise we require that we never create a ValueNumber with the TARGET_SIGN_BIT set.
//
@@ -709,7 +710,6 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
C_ASSERT((signed char)MAX_CSE_CNT == MAX_CSE_CNT);
unsigned CSEindex = ++optCSECandidateCount;
- // EXPSET_TP CSEmask = genCSEnum2bit(CSEindex);
/* Record the new CSE index in the hashDsc */
hashDsc->csdIndex = CSEindex;
@@ -746,16 +746,14 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
}
}
-/*****************************************************************************
- *
- * Locate CSE candidates and assign indices to them
- * return 0 if no CSE candidates were found
- */
-
-unsigned Compiler::optValnumCSE_Locate()
+//------------------------------------------------------------------------
+// optValnumCSE_Locate: Locate CSE candidates and assign them indices.
+//
+// Returns:
+// true if there are any CSE candidates, false otherwise
+//
+bool Compiler::optValnumCSE_Locate()
{
- // Locate CSE candidates and assign them indices
-
bool enableConstCSE = true;
int configValue = JitConfig.JitConstCSE();
@@ -871,14 +869,14 @@ unsigned Compiler::optValnumCSE_Locate()
if (!optDoCSE)
{
- return 0;
+ return false;
}
/* We're finished building the expression lookup table */
optCSEstop();
- return 1;
+ return true;
}
//------------------------------------------------------------------------
@@ -890,7 +888,7 @@ unsigned Compiler::optValnumCSE_Locate()
//
// Arguments:
// compare - The compare node to check
-
+//
void Compiler::optCseUpdateCheckedBoundMap(GenTree* compare)
{
assert(compare->OperIsCompare());
@@ -999,9 +997,9 @@ void Compiler::optValnumCSE_InitDataFlow()
// Init traits and cseCallKillsMask bitvectors.
cseLivenessTraits = new (getAllocator(CMK_CSE)) BitVecTraits(bitCount, this);
cseCallKillsMask = BitVecOps::MakeEmpty(cseLivenessTraits);
- for (unsigned inx = 0; inx < optCSECandidateCount; inx++)
+ for (unsigned inx = 1; inx <= optCSECandidateCount; inx++)
{
- unsigned cseAvailBit = inx * 2;
+ unsigned cseAvailBit = getCSEAvailBit(inx);
// a one preserves availability and a zero kills the availability
// we generate this kind of bit pattern: 101010101010
@@ -1047,7 +1045,7 @@ void Compiler::optValnumCSE_InitDataFlow()
block->bbCseGen = BitVecOps::MakeEmpty(cseLivenessTraits);
}
- // We walk the set of CSE candidates and set the bit corresponsing to the CSEindex
+ // We walk the set of CSE candidates and set the bit corresponding to the CSEindex
// in the block's bbCseGen bitset
//
for (unsigned inx = 0; inx < optCSECandidateCount; inx++)
@@ -1060,16 +1058,16 @@ void Compiler::optValnumCSE_InitDataFlow()
while (lst != nullptr)
{
BasicBlock* block = lst->tslBlock;
- unsigned CseAvailBit = genCSEnum2bit(CSEindex) * 2;
- unsigned cseAvailCrossCallBit = CseAvailBit + 1;
+ unsigned cseAvailBit = getCSEAvailBit(CSEindex);
+ unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEindex);
- // This CSE is generated in 'block', we always set the CseAvailBit
+ // This CSE is generated in 'block', we always set the cseAvailBit
// If this block does not contain a call, we also set cseAvailCrossCallBit
//
// If we have a call in this block then in the loop below we walk the trees
// backwards to find any CSEs that are generated after the last call in the block.
//
- BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, CseAvailBit);
+ BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailBit);
if ((block->bbFlags & BBF_HAS_CALL) == 0)
{
BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit);
@@ -1113,7 +1111,7 @@ void Compiler::optValnumCSE_InitDataFlow()
if (IS_CSE_INDEX(tree->gtCSEnum))
{
unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum);
- unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1;
+ unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum);
BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, cseAvailCrossCallBit);
}
if (tree->OperGet() == GT_CALL)
@@ -1142,15 +1140,16 @@ void Compiler::optValnumCSE_InitDataFlow()
bool headerPrinted = false;
for (BasicBlock* const block : Blocks())
{
- if (block->bbCseGen != nullptr)
+ if (!BitVecOps::IsEmpty(cseLivenessTraits, block->bbCseGen))
{
if (!headerPrinted)
{
printf("\nBlocks that generate CSE def/uses\n");
headerPrinted = true;
}
- printf(FMT_BB, block->bbNum);
- printf(" cseGen = %s\n", genES2str(cseLivenessTraits, block->bbCseGen));
+ printf(FMT_BB " cseGen = ", block->bbNum);
+ optPrintCSEDataFlowSet(block->bbCseGen);
+ printf("\n");
}
}
}
@@ -1184,6 +1183,7 @@ class CSE_DataFlow
//
BitVecOps::Assign(m_comp->cseLivenessTraits, m_preMergeOut, block->bbCseOut);
+#if 0
#ifdef DEBUG
if (m_comp->verbose)
{
@@ -1191,11 +1191,13 @@ class CSE_DataFlow
printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut));
}
#endif // DEBUG
+#endif // 0
}
// Merge: perform the merging of each of the predecessor's liveness values (since this is a forward analysis)
void Merge(BasicBlock* block, BasicBlock* predBlock, unsigned dupCount)
{
+#if 0
#ifdef DEBUG
if (m_comp->verbose)
{
@@ -1204,15 +1206,18 @@ class CSE_DataFlow
printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut));
}
#endif // DEBUG
+#endif // 0
BitVecOps::IntersectionD(m_comp->cseLivenessTraits, block->bbCseIn, predBlock->bbCseOut);
+#if 0
#ifdef DEBUG
if (m_comp->verbose)
{
printf(" => cseIn = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseIn));
}
#endif // DEBUG
+#endif // 0
}
//------------------------------------------------------------------------
@@ -1272,6 +1277,7 @@ class CSE_DataFlow
//
bool notDone = !BitVecOps::Equal(m_comp->cseLivenessTraits, block->bbCseOut, m_preMergeOut);
+#if 0
#ifdef DEBUG
if (m_comp->verbose)
{
@@ -1288,6 +1294,7 @@ class CSE_DataFlow
notDone ? "true" : "false");
}
#endif // DEBUG
+#endif // 0
return notDone;
}
@@ -1330,10 +1337,12 @@ void Compiler::optValnumCSE_DataFlow()
for (BasicBlock* const block : Blocks())
{
- printf(FMT_BB, block->bbNum);
- printf(" cseIn = %s,", genES2str(cseLivenessTraits, block->bbCseIn));
- printf(" cseGen = %s,", genES2str(cseLivenessTraits, block->bbCseGen));
- printf(" cseOut = %s", genES2str(cseLivenessTraits, block->bbCseOut));
+ printf(FMT_BB " in gen out\n", block->bbNum);
+ optPrintCSEDataFlowSet(block->bbCseIn);
+ printf("\n");
+ optPrintCSEDataFlowSet(block->bbCseGen);
+ printf("\n");
+ optPrintCSEDataFlowSet(block->bbCseOut);
printf("\n");
}
@@ -1347,17 +1356,17 @@ void Compiler::optValnumCSE_DataFlow()
//
// Using the information computed by CSE_DataFlow determine for each
// CSE whether the CSE is a definition (if the CSE was not available)
-// or if the CSE is a use (if the CSE was previously made available)
-// The implementation iterates of all blocks setting 'available_cses'
+// or if the CSE is a use (if the CSE was previously made available).
+// The implementation iterates over all blocks setting 'available_cses'
// to the CSEs that are available at input to the block.
// When a CSE expression is encountered it is classified as either
// as a definition (if the CSE is not in the 'available_cses' set) or
-// as a use (if the CSE is in the 'available_cses' set). If the CSE
+// as a use (if the CSE is in the 'available_cses' set). If the CSE
// is a definition then it is added to the 'available_cses' set.
//
// This algorithm uncovers the defs and uses gradually and as it does
// so it also builds the exception set that all defs make: 'defExcSetCurrent'
-// and the exception set that the uses we have seen depend upon: 'defExcSetPromise'
+// and the exception set that the uses we have seen depend upon: 'defExcSetPromise'.
//
// Typically expressions with the same normal ValueNum generate exactly the
// same exception sets. There are two way that we can get different exception
@@ -1371,12 +1380,11 @@ void Compiler::optValnumCSE_DataFlow()
// 2. We stored an expression into a LclVar or into Memory and read it later
// e.g. t = p.a;
// e1 = (t + q.b) :: e1 has one NullPtrExc and e2 has two.
-// e2 = (p.a + q.b) but both compute the same normal value//
+// e2 = (p.a + q.b) but both compute the same normal value
// e.g. m.a = p.a;
// e1 = (m.a + q.b) :: e1 and e2 have different exception sets.
// e2 = (p.a + q.b) but both compute the same normal value
//
-//
void Compiler::optValnumCSE_Availablity()
{
#ifdef DEBUG
@@ -1411,12 +1419,12 @@ void Compiler::optValnumCSE_Availablity()
if (IS_CSE_INDEX(tree->gtCSEnum))
{
unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum);
- unsigned CseAvailBit = genCSEnum2bit(CSEnum) * 2;
- unsigned cseAvailCrossCallBit = CseAvailBit + 1;
+ unsigned cseAvailBit = getCSEAvailBit(CSEnum);
+ unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum);
CSEdsc* desc = optCSEfindDsc(CSEnum);
BasicBlock::weight_t stmw = block->getBBWeight(this);
- isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, CseAvailBit);
+ isUse = BitVecOps::IsMember(cseLivenessTraits, available_cses, cseAvailBit);
isDef = !isUse; // If is isn't a CSE use, it is a CSE def
// Is this a "use", that we haven't yet marked as live across a call
@@ -1446,7 +1454,7 @@ void Compiler::optValnumCSE_Availablity()
printf(FMT_BB " ", block->bbNum);
printTreeID(tree);
- printf(" %s of CSE #%02u [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw),
+ printf(" %s of " FMT_CSE " [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw),
madeLiveAcrossCall ? " *** Now Live Across Call ***" : "");
}
#endif // DEBUG
@@ -1477,7 +1485,7 @@ void Compiler::optValnumCSE_Availablity()
// Is defExcSetCurrent still set to the uninit marker value of VNForNull() ?
if (desc->defExcSetCurrent == vnStore->VNForNull())
{
- // This is the first time visited, so record this defs exeception set
+ // This is the first time visited, so record this defs exception set
desc->defExcSetCurrent = theLiberalExcSet;
}
@@ -1589,7 +1597,7 @@ void Compiler::optValnumCSE_Availablity()
tree->gtCSEnum = TO_CSE_DEF(tree->gtCSEnum);
// This CSE becomes available after this def
- BitVecOps::AddElemD(cseLivenessTraits, available_cses, CseAvailBit);
+ BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailBit);
BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit);
}
else // We are visiting a CSE use
@@ -1636,7 +1644,7 @@ void Compiler::optValnumCSE_Availablity()
if (!vnStore->VNExcIsSubset(desc->defExcSetPromise, theLiberalExcSet))
{
// We can't safely make this into a CSE use, because this
- // CSE use has an exeception set item that is not promised
+ // CSE use has an exception set item that is not promised
// by all of our CSE defs.
//
// We will omit this CSE use from the graph and proceed,
@@ -1660,7 +1668,7 @@ void Compiler::optValnumCSE_Availablity()
// In order to determine if a CSE is live across a call, we model availablity using two bits and
// kill all of the cseAvailCrossCallBit for each CSE whenever we see a GT_CALL (unless the call
- // generates A cse)
+ // generates a CSE).
//
if (tree->OperGet() == GT_CALL)
{
@@ -1690,7 +1698,7 @@ void Compiler::optValnumCSE_Availablity()
// available_cses
//
unsigned CSEnum = GET_CSE_INDEX(tree->gtCSEnum);
- unsigned cseAvailCrossCallBit = (genCSEnum2bit(CSEnum) * 2) + 1;
+ unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(CSEnum);
BitVecOps::AddElemD(cseLivenessTraits, available_cses, cseAvailCrossCallBit);
}
@@ -2027,14 +2035,14 @@ class CSE_Heuristic
if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey))
{
- printf("CSE #%02u, {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ",
+ printf(FMT_CSE ", {$%-3x, $%-3x} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ",
dsc->csdIndex, dsc->csdHashKey, dsc->defExcSetPromise, dsc->csdUseCount, def, use, cost,
dsc->csdLiveAcrossCall ? ", call" : " ");
}
else
{
size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey);
- printf("CSE #%02u, {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex,
+ printf(FMT_CSE ", {K_%p} useCnt=%d: [def=%3f, use=%3f, cost=%3u%s]\n :: ", dsc->csdIndex,
dspPtr(kVal), dsc->csdUseCount, def, use, cost,
dsc->csdLiveAcrossCall ? ", call" : " ");
}
@@ -2814,7 +2822,7 @@ class CSE_Heuristic
if (dsc->csdDefCount == 1)
{
- JITDUMP("CSE #%02u is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex,
+ JITDUMP(FMT_CSE " is single-def, so associated CSE temp V%02u will be in SSA\n", dsc->csdIndex,
cseLclVarNum);
m_pCompiler->lvaTable[cseLclVarNum].lvInSsa = true;
@@ -2931,7 +2939,7 @@ class CSE_Heuristic
if (IS_CSE_INDEX(lst->tslTree->gtCSEnum))
{
ValueNum currVN = m_pCompiler->vnStore->VNLiberalNormalValue(lst->tslTree->gtVNPair);
- printf("0x%x(%s " FMT_VN ") ", lst->tslTree,
+ printf("[%06d](%s " FMT_VN ") ", m_pCompiler->dspTreeID(lst->tslTree),
IS_CSE_USE(lst->tslTree->gtCSEnum) ? "use" : "def", currVN);
}
lst = lst->tslNext;
@@ -2996,7 +3004,7 @@ class CSE_Heuristic
#ifdef DEBUG
if (m_pCompiler->verbose)
{
- printf("\nWorking on the replacement of the CSE #%02u use at ", exp->gtCSEnum);
+ printf("\nWorking on the replacement of the " FMT_CSE " use at ", exp->gtCSEnum);
Compiler::printTreeID(exp);
printf(" in " FMT_BB "\n", blk->bbNum);
}
@@ -3164,7 +3172,7 @@ class CSE_Heuristic
#ifdef DEBUG
if (m_pCompiler->verbose)
{
- printf("\nCSE #%02u def at ", GET_CSE_INDEX(exp->gtCSEnum));
+ printf("\n" FMT_CSE " def at ", GET_CSE_INDEX(exp->gtCSEnum));
Compiler::printTreeID(exp);
printf(" replaced in " FMT_BB " with def of V%02u\n", blk->bbNum, cseLclVarNum);
}
@@ -3314,13 +3322,13 @@ class CSE_Heuristic
if (dsc->defExcSetPromise == ValueNumStore::NoVN)
{
- JITDUMP("Abandoned CSE #%02u because we had defs with different Exc sets\n", candidate.CseIndex());
+ JITDUMP("Abandoned " FMT_CSE " because we had defs with different Exc sets\n", candidate.CseIndex());
continue;
}
if (dsc->csdStructHndMismatch)
{
- JITDUMP("Abandoned CSE #%02u because we had mismatching struct handles\n", candidate.CseIndex());
+ JITDUMP("Abandoned " FMT_CSE " because we had mismatching struct handles\n", candidate.CseIndex());
continue;
}
@@ -3328,7 +3336,7 @@ class CSE_Heuristic
if (candidate.UseCount() == 0)
{
- JITDUMP("Skipped CSE #%02u because use count is 0\n", candidate.CseIndex());
+ JITDUMP("Skipped " FMT_CSE " because use count is 0\n", candidate.CseIndex());
continue;
}
@@ -3337,14 +3345,14 @@ class CSE_Heuristic
{
if (!Compiler::Is_Shared_Const_CSE(dsc->csdHashKey))
{
- printf("\nConsidering CSE #%02u {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n",
+ printf("\nConsidering " FMT_CSE " {$%-3x, $%-3x} [def=%3f, use=%3f, cost=%3u%s]\n",
candidate.CseIndex(), dsc->csdHashKey, dsc->defExcSetPromise, candidate.DefCount(),
candidate.UseCount(), candidate.Cost(), dsc->csdLiveAcrossCall ? ", call" : " ");
}
else
{
size_t kVal = Compiler::Decode_Shared_Const_CSE_Value(dsc->csdHashKey);
- printf("\nConsidering CSE #%02u {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(),
+ printf("\nConsidering " FMT_CSE " {K_%p} [def=%3f, use=%3f, cost=%3u%s]\n", candidate.CseIndex(),
dspPtr(kVal), candidate.DefCount(), candidate.UseCount(), candidate.Cost(),
dsc->csdLiveAcrossCall ? ", call" : " ");
}
@@ -3428,11 +3436,6 @@ void Compiler::optValnumCSE_Heuristic()
void Compiler::optOptimizeValnumCSEs()
{
#ifdef DEBUG
- if (verbose)
- {
- printf("\n*************** In optOptimizeValnumCSEs()\n");
- }
-
if (optConfigDisableCSE())
{
return; // Disabled by JitNoCSE
@@ -3441,22 +3444,13 @@ void Compiler::optOptimizeValnumCSEs()
optValnumCSE_phase = true;
- /* Initialize the expression tracking logic */
-
optValnumCSE_Init();
- /* Locate interesting expressions and assign indices to them */
-
- if (optValnumCSE_Locate() > 0)
+ if (optValnumCSE_Locate())
{
- optCSECandidateTotal += optCSECandidateCount;
-
optValnumCSE_InitDataFlow();
-
optValnumCSE_DataFlow();
-
optValnumCSE_Availablity();
-
optValnumCSE_Heuristic();
}
@@ -3807,21 +3801,11 @@ bool Compiler::optConfigDisableCSE2()
void Compiler::optOptimizeCSEs()
{
-#ifdef DEBUG
- if (verbose)
- {
- printf("\n*************** In optOptimizeCSEs()\n");
- printf("Blocks/Trees at start of optOptimizeCSE phase\n");
- fgDispBasicBlocks(true);
- }
-#endif // DEBUG
-
optCSECandidateCount = 0;
optCSEstart = lvaCount;
INDEBUG(optEnsureClearCSEInfo());
optOptimizeValnumCSEs();
- EndPhase(PHASE_OPTIMIZE_VALNUM_CSES);
}
/*****************************************************************************
@@ -3873,4 +3857,38 @@ void Compiler::optEnsureClearCSEInfo()
}
}
+//------------------------------------------------------------------------
+// optPrintCSEDataFlowSet: Print out one of the CSE dataflow sets bbCseGen, bbCseIn, bbCseOut,
+// interpreting the bits in a more useful way for the dump.
+//
+// Arguments:
+// cseDataFlowSet - One of the dataflow sets to display
+// includeBits - Display the actual bits of the set as well
+//
+void Compiler::optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits /* = true */)
+{
+ if (includeBits)
+ {
+ printf("%s ", genES2str(cseLivenessTraits, cseDataFlowSet));
+ }
+
+ bool first = true;
+ for (unsigned cseIndex = 1; cseIndex <= optCSECandidateCount; cseIndex++)
+ {
+ unsigned cseAvailBit = getCSEAvailBit(cseIndex);
+ unsigned cseAvailCrossCallBit = getCSEAvailCrossCallBit(cseIndex);
+
+ if (BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailBit))
+ {
+ if (!first)
+ {
+ printf(", ");
+ }
+ const bool isAvailCrossCall = BitVecOps::IsMember(cseLivenessTraits, cseDataFlowSet, cseAvailCrossCallBit);
+ printf(FMT_CSE "%s", cseIndex, isAvailCrossCall ? ".c" : "");
+ first = false;
+ }
+ }
+}
+
#endif // DEBUG
diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp
index a7df5b95905a2..748d5feabb562 100644
--- a/src/coreclr/jit/optimizer.cpp
+++ b/src/coreclr/jit/optimizer.cpp
@@ -38,7 +38,6 @@ void Compiler::optInit()
optNativeCallCount = 0;
optAssertionCount = 0;
optAssertionDep = nullptr;
- optCSECandidateTotal = 0;
optCSEstart = UINT_MAX;
optCSEcount = 0;
}
@@ -5627,8 +5626,8 @@ void Compiler::optHoistLoopCode()
#endif
#if 0
- // The code in this #if has been useful in debugging loop cloning issues, by
- // enabling selective enablement of the loop cloning optimization according to
+ // The code in this #if has been useful in debugging loop hoisting issues, by
+ // enabling selective enablement of the loop hoisting optimization according to
// method hash.
#ifdef DEBUG
unsigned methHash = info.compMethodHash();
@@ -5650,7 +5649,7 @@ void Compiler::optHoistLoopCode()
return;
printf("Doing loop hoisting in %s (0x%x).\n", info.compFullName, methHash);
#endif // DEBUG
-#endif // 0 -- debugging loop cloning issues
+#endif // 0 -- debugging loop hoisting issues
#ifdef DEBUG
if (verbose)
@@ -5899,6 +5898,8 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt)
printf("\n LOOPV-FP(%d)=", pLoopDsc->lpLoopVarFPCount);
lvaDispVarSet(loopFPVars);
+
+ printf("\n");
}
#endif
}
diff --git a/src/coreclr/jit/phase.cpp b/src/coreclr/jit/phase.cpp
index 530f8e74cbba0..f08134bf11532 100644
--- a/src/coreclr/jit/phase.cpp
+++ b/src/coreclr/jit/phase.cpp
@@ -84,8 +84,9 @@ void Phase::PrePhase()
//
// Currently the list is just the set of phases that have custom
// derivations from the Phase class.
- static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_RATIONALIZE, PHASE_LOWERING, PHASE_STACK_LEVEL_SETTER};
- bool doPrePhase = false;
+ static Phases s_allowlist[] = {PHASE_BUILD_SSA, PHASE_OPTIMIZE_VALNUM_CSES, PHASE_RATIONALIZE, PHASE_LOWERING,
+ PHASE_STACK_LEVEL_SETTER};
+ bool doPrePhase = false;
for (size_t i = 0; i < sizeof(s_allowlist) / sizeof(Phases); i++)
{
diff --git a/src/coreclr/minipal/CMakeLists.txt b/src/coreclr/minipal/CMakeLists.txt
new file mode 100644
index 0000000000000..3096237d2a2fe
--- /dev/null
+++ b/src/coreclr/minipal/CMakeLists.txt
@@ -0,0 +1,7 @@
+include_directories(.)
+if (CLR_CMAKE_HOST_UNIX)
+ add_subdirectory(Unix)
+else (CLR_CMAKE_HOST_UNIX)
+ add_subdirectory(Windows)
+endif (CLR_CMAKE_HOST_UNIX)
+
diff --git a/src/coreclr/minipal/Unix/CMakeLists.txt b/src/coreclr/minipal/Unix/CMakeLists.txt
new file mode 100644
index 0000000000000..b56b5017d375f
--- /dev/null
+++ b/src/coreclr/minipal/Unix/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(coreclrminipal
+ STATIC
+ doublemapping.cpp
+)
diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp
new file mode 100644
index 0000000000000..6e0278e3ccd69
--- /dev/null
+++ b/src/coreclr/minipal/Unix/doublemapping.cpp
@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if defined(TARGET_LINUX) && !defined(MFD_CLOEXEC)
+#include
+#include // __NR_memfd_create
+#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__)
+#endif // TARGET_LINUX && !MFD_CLOEXEC
+#include "minipal.h"
+
+#if defined(TARGET_OSX) && defined(TARGET_AMD64)
+#include
+#endif // TARGET_OSX && TARGET_AMD64
+
+#ifndef TARGET_OSX
+
+#ifdef TARGET_64BIT
+static const off_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024;
+#else
+static const off_t MaxDoubleMappedSize = UINT_MAX;
+#endif
+
+#endif // TARGET_OSX
+
+bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize)
+{
+#ifndef TARGET_OSX
+
+#ifdef TARGET_FREEBSD
+ int fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, S_IRWXU);
+#else // TARGET_FREEBSD
+ int fd = memfd_create("doublemapper", MFD_CLOEXEC);
+#endif // TARGET_FREEBSD
+
+ if (fd == -1)
+ {
+ return false;
+ }
+
+ if (ftruncate(fd, MaxDoubleMappedSize) == -1)
+ {
+ close(fd);
+ return false;
+ }
+
+ *pMaxExecutableCodeSize = MaxDoubleMappedSize;
+ *pHandle = (void*)(size_t)fd;
+#else // !TARGET_OSX
+ *pMaxExecutableCodeSize = SIZE_MAX;
+ *pHandle = NULL;
+#endif // !TARGET_OSX
+
+ return true;
+}
+
+void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle)
+{
+#ifndef TARGET_OSX
+ close((int)(size_t)mapperHandle);
+#endif
+}
+
+extern "C" void* PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(const void* lpBeginAddress, const void* lpEndAddress, size_t dwSize);
+
+#ifdef TARGET_OSX
+bool IsMapJitFlagNeeded()
+{
+ static volatile int isMapJitFlagNeeded = -1;
+
+ if (isMapJitFlagNeeded == -1)
+ {
+ int mapJitFlagCheckResult = 0;
+ int pageSize = sysconf(_SC_PAGE_SIZE);
+ // Try to map a page with read-write-execute protection. It should fail on Mojave hardened runtime and higher.
+ void* testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (testPage == MAP_FAILED && (errno == EACCES))
+ {
+ // The mapping has failed with EACCES, check if making the same mapping with MAP_JIT flag works
+ testPage = mmap(NULL, pageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_JIT, -1, 0);
+ if (testPage != MAP_FAILED)
+ {
+ mapJitFlagCheckResult = 1;
+ }
+ }
+
+ if (testPage != MAP_FAILED)
+ {
+ munmap(testPage, pageSize);
+ }
+
+ isMapJitFlagNeeded = mapJitFlagCheckResult;
+ }
+
+ return (bool)isMapJitFlagNeeded;
+}
+#endif // TARGET_OSX
+
+void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd)
+{
+ int fd = (int)(size_t)mapperHandle;
+
+ if (rangeStart != NULL || rangeEnd != NULL)
+ {
+ void* result = PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(rangeStart, rangeEnd, size);
+#ifndef TARGET_OSX
+ if (result != NULL)
+ {
+ // Map the shared memory over the range reserved from the executable memory allocator.
+ result = mmap(result, size, PROT_NONE, MAP_SHARED | MAP_FIXED, fd, offset);
+ if (result == MAP_FAILED)
+ {
+ assert(false);
+ result = NULL;
+ }
+ }
+#endif // TARGET_OSX
+
+ return result;
+ }
+
+#ifndef TARGET_OSX
+ void* result = mmap(NULL, size, PROT_NONE, MAP_SHARED, fd, offset);
+#else
+ int mmapFlags = MAP_ANON | MAP_PRIVATE;
+ if (IsMapJitFlagNeeded())
+ {
+ mmapFlags |= MAP_JIT;
+ }
+ void* result = mmap(NULL, size, PROT_NONE, mmapFlags, -1, 0);
+#endif
+ if (result == MAP_FAILED)
+ {
+ assert(false);
+ result = NULL;
+ }
+ return result;
+}
+
+void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable)
+{
+ if (mprotect(pStart, size, isExecutable ? (PROT_READ | PROT_EXEC) : (PROT_READ | PROT_WRITE)) == -1)
+ {
+ return NULL;
+ }
+
+ return pStart;
+}
+
+bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size)
+{
+#ifndef TARGET_OSX
+ int fd = (int)(size_t)mapperHandle;
+ mmap(pStart, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset);
+ memset(pStart, 0, size);
+#endif // TARGET_OSX
+ return munmap(pStart, size) != -1;
+}
+
+void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size)
+{
+#ifndef TARGET_OSX
+ int fd = (int)(size_t)mapperHandle;
+ return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
+#else // TARGET_OSX
+#ifdef TARGET_AMD64
+ vm_address_t startRW;
+ vm_prot_t curProtection, maxProtection;
+ kern_return_t kr = vm_remap(mach_task_self(), &startRW, size, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR,
+ mach_task_self(), (vm_address_t)pStart, FALSE, &curProtection, &maxProtection, VM_INHERIT_NONE);
+
+ if (kr != KERN_SUCCESS)
+ {
+ return NULL;
+ }
+
+ int st = mprotect((void*)startRW, size, PROT_READ | PROT_WRITE);
+ if (st == -1)
+ {
+ munmap((void*)startRW, size);
+ return NULL;
+ }
+
+ return (void*)startRW;
+#else // TARGET_AMD64
+ // This method should not be called on OSX ARM64
+ assert(false);
+ return NULL;
+#endif // TARGET_AMD64
+#endif // TARGET_OSX
+}
+
+bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size)
+{
+ return munmap(pStart, size) != -1;
+}
diff --git a/src/coreclr/minipal/Windows/CMakeLists.txt b/src/coreclr/minipal/Windows/CMakeLists.txt
new file mode 100644
index 0000000000000..b56b5017d375f
--- /dev/null
+++ b/src/coreclr/minipal/Windows/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(coreclrminipal
+ STATIC
+ doublemapping.cpp
+)
diff --git a/src/coreclr/minipal/Windows/doublemapping.cpp b/src/coreclr/minipal/Windows/doublemapping.cpp
new file mode 100644
index 0000000000000..e265f1d139ad0
--- /dev/null
+++ b/src/coreclr/minipal/Windows/doublemapping.cpp
@@ -0,0 +1,205 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+#include
+#include
+#include
+#include "minipal.h"
+
+#define HIDWORD(_qw) ((ULONG)((_qw) >> 32))
+#define LODWORD(_qw) ((ULONG)(_qw))
+
+#ifdef TARGET_64BIT
+static const uint64_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024;
+#else
+static const uint64_t MaxDoubleMappedSize = UINT_MAX;
+#endif
+
+#define VIRTUAL_ALLOC_RESERVE_GRANULARITY (64*1024) // 0x10000 (64 KB)
+inline size_t ALIGN_UP( size_t val, size_t alignment )
+{
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ assert( 0 == (alignment & (alignment - 1)) );
+ size_t result = (val + (alignment - 1)) & ~(alignment - 1);
+ assert( result >= val ); // check for overflow
+ return result;
+}
+
+template inline T ALIGN_UP(T val, size_t alignment)
+{
+ return (T)ALIGN_UP((size_t)val, alignment);
+}
+
+inline void *GetTopMemoryAddress(void)
+{
+ static void *result; // = NULL;
+ if( NULL == result )
+ {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo( &sysInfo );
+ result = sysInfo.lpMaximumApplicationAddress;
+ }
+ return result;
+}
+
+inline void *GetBotMemoryAddress(void)
+{
+ static void *result; // = NULL;
+ if( NULL == result )
+ {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo( &sysInfo );
+ result = sysInfo.lpMinimumApplicationAddress;
+ }
+ return result;
+}
+
+#define TOP_MEMORY (GetTopMemoryAddress())
+#define BOT_MEMORY (GetBotMemoryAddress())
+
+bool VMToOSInterface::CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize)
+{
+ *pMaxExecutableCodeSize = (size_t)MaxDoubleMappedSize;
+ *pHandle = CreateFileMapping(
+ INVALID_HANDLE_VALUE, // use paging file
+ NULL, // default security
+ PAGE_EXECUTE_READWRITE | SEC_RESERVE, // read/write/execute access
+ HIDWORD(MaxDoubleMappedSize), // maximum object size (high-order DWORD)
+ LODWORD(MaxDoubleMappedSize), // maximum object size (low-order DWORD)
+ NULL);
+
+ return *pHandle != NULL;
+}
+
+void VMToOSInterface::DestroyDoubleMemoryMapper(void *mapperHandle)
+{
+ CloseHandle((HANDLE)mapperHandle);
+}
+
+void* VMToOSInterface::ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *pMinAddr, const void* pMaxAddr)
+{
+ BYTE *pResult = nullptr; // our return value;
+
+ if (size == 0)
+ {
+ return nullptr;
+ }
+
+ //
+ // First lets normalize the pMinAddr and pMaxAddr values
+ //
+ // If pMinAddr is NULL then set it to BOT_MEMORY
+ if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY))
+ {
+ pMinAddr = (BYTE *) BOT_MEMORY;
+ }
+
+ // If pMaxAddr is NULL then set it to TOP_MEMORY
+ if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY))
+ {
+ pMaxAddr = (BYTE *) TOP_MEMORY;
+ }
+
+ // If pMaxAddr is not greater than pMinAddr we can not make an allocation
+ if (pMaxAddr <= pMinAddr)
+ {
+ return nullptr;
+ }
+
+ // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
+ // then we can call ClrVirtualAlloc instead
+ if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
+ {
+ return (BYTE*)MapViewOfFile((HANDLE)mapperHandle,
+ FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE,
+ HIDWORD((int64_t)offset),
+ LODWORD((int64_t)offset),
+ size);
+ }
+
+ // We will do one scan from [pMinAddr .. pMaxAddr]
+ // First align the tryAddr up to next 64k base address.
+ // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
+ //
+ BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+ bool virtualQueryFailed = false;
+ bool faultInjected = false;
+ unsigned virtualQueryCount = 0;
+
+ // Now scan memory and try to find a free block of the size requested.
+ while ((tryAddr + size) <= (BYTE *) pMaxAddr)
+ {
+ MEMORY_BASIC_INFORMATION mbInfo;
+
+ // Use VirtualQuery to find out if this address is MEM_FREE
+ //
+ virtualQueryCount++;
+ if (!VirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
+ {
+ // Exit and return nullptr if the VirtualQuery call fails.
+ virtualQueryFailed = true;
+ break;
+ }
+
+ // Is there enough memory free from this start location?
+ // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0
+ if ((mbInfo.State == MEM_FREE) &&
+ (mbInfo.RegionSize >= (SIZE_T) size || mbInfo.RegionSize == 0))
+ {
+ // Try reserving the memory using VirtualAlloc now
+ pResult = (BYTE*)MapViewOfFileEx((HANDLE)mapperHandle,
+ FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE,
+ HIDWORD((int64_t)offset),
+ LODWORD((int64_t)offset),
+ size,
+ tryAddr);
+
+ // Normally this will be successful
+ //
+ if (pResult != nullptr)
+ {
+ // return pResult
+ break;
+ }
+
+ // We might fail in a race. So just move on to next region and continue trying
+ tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
+ }
+ else
+ {
+ // Try another section of memory
+ tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
+ (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
+ }
+ }
+
+ return pResult;
+}
+
+void *VMToOSInterface::CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable)
+{
+ return VirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READ : PAGE_READWRITE);
+}
+
+bool VMToOSInterface::ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size)
+{
+ // Zero the memory before the unmapping
+ VirtualAlloc(pStart, size, MEM_COMMIT, PAGE_READWRITE);
+ memset(pStart, 0, size);
+ return UnmapViewOfFile(pStart);
+}
+
+void* VMToOSInterface::GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size)
+{
+ return (BYTE*)MapViewOfFile((HANDLE)mapperHandle,
+ FILE_MAP_READ | FILE_MAP_WRITE,
+ HIDWORD((int64_t)offset),
+ LODWORD((int64_t)offset),
+ size);
+}
+
+bool VMToOSInterface::ReleaseRWMapping(void* pStart, size_t size)
+{
+ return UnmapViewOfFile(pStart);
+}
diff --git a/src/coreclr/minipal/minipal.h b/src/coreclr/minipal/minipal.h
new file mode 100644
index 0000000000000..39098f9bc1295
--- /dev/null
+++ b/src/coreclr/minipal/minipal.h
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+#include
+
+// Interface between the runtime and platform specific functionality
+class VMToOSInterface
+{
+private:
+ ~VMToOSInterface() {}
+public:
+ // Create double mapped memory mapper
+ // Parameters:
+ // pHandle - receives handle of the double mapped memory mapper
+ // pMaxExecutableCodeSize - receives the maximum executable memory size it can map
+ // Return:
+ // true if it succeeded, false if it failed
+ static bool CreateDoubleMemoryMapper(void **pHandle, size_t *pMaxExecutableCodeSize);
+
+ // Destroy the double mapped memory mapper represented by the passed in handle
+ // Parameters:
+ // mapperHandle - handle of the double mapped memory mapper to destroy
+ static void DestroyDoubleMemoryMapper(void *mapperHandle);
+
+ // Reserve a block of memory that can be double mapped.
+ // Parameters:
+ // mapperHandle - handle of the double mapped memory mapper to use
+ // offset - offset in the underlying shared memory
+ // size - size of the block to reserve
+ // rangeStart
+ // rangeEnd - Requests reserving virtual memory in the specified range.
+ // Setting both rangeStart and rangeEnd to 0 means that the
+ // requested range is not limited.
+ // When a specific range is requested, it is obligatory.
+ // Return:
+ // starting virtual address of the reserved memory or NULL if it failed
+ static void* ReserveDoubleMappedMemory(void *mapperHandle, size_t offset, size_t size, const void *rangeStart, const void* rangeEnd);
+
+ // Commit a block of memory in the range previously reserved by the ReserveDoubleMappedMemory
+ // Parameters:
+ // pStart - start address of the virtual address range to commit
+ // size - size of the memory block to commit
+ // isExecutable - true means that the mapping should be RX, false means RW
+ // Return:
+ // Committed range start
+ static void* CommitDoubleMappedMemory(void* pStart, size_t size, bool isExecutable);
+
+ // Release a block of virtual memory previously commited by the CommitDoubleMappedMemory
+ // Parameters:
+ // mapperHandle - handle of the double mapped memory mapper to use
+ // pStart - start address of the virtual address range to release. It must be one
+ // that was previously returned by the CommitDoubleMappedMemory
+ // offset - offset in the underlying shared memory
+ // size - size of the memory block to release
+ // Return:
+ // true if it succeeded, false if it failed
+ static bool ReleaseDoubleMappedMemory(void *mapperHandle, void* pStart, size_t offset, size_t size);
+
+ // Get a RW mapping for the RX block specified by the arguments
+ // Parameters:
+ // mapperHandle - handle of the double mapped memory mapper to use
+ // pStart - start address of the RX virtual address range.
+ // offset - offset in the underlying shared memory
+ // size - size of the memory block to map as RW
+ // Return:
+ // Starting virtual address of the RW mapping.
+ static void* GetRWMapping(void *mapperHandle, void* pStart, size_t offset, size_t size);
+
+ // Release RW mapping of the block specified by the arguments
+ // Parameters:
+ // pStart - Start address of the RW virtual address range. It must be an address
+ // previously returned by the GetRWMapping.
+ // size - Size of the memory block to release. It must be the size previously
+ // passed to the GetRWMapping that returned the pStart.
+ // Return:
+ // true if it succeeded, false if it failed
+ static bool ReleaseRWMapping(void* pStart, size_t size);
+};
diff --git a/src/coreclr/pal/prebuilt/inc/corprof.h b/src/coreclr/pal/prebuilt/inc/corprof.h
index 85ce86870bf8c..e82623d0c09f0 100644
--- a/src/coreclr/pal/prebuilt/inc/corprof.h
+++ b/src/coreclr/pal/prebuilt/inc/corprof.h
@@ -574,6 +574,7 @@ enum __MIDL___MIDL_itf_corprof_0000_0000_0006
COR_PRF_HIGH_REQUIRE_PROFILE_IMAGE = 0,
COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED = 0x40,
COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x80,
+ COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x100,
COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) ,
COR_PRF_HIGH_ALLOWABLE_NOTIFICATION_PROFILER = ( ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_DISABLE_TIERED_COMPILATION ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) ,
COR_PRF_HIGH_MONITOR_IMMUTABLE = COR_PRF_HIGH_DISABLE_TIERED_COMPILATION
diff --git a/src/coreclr/pal/prebuilt/inc/xclrdata.h b/src/coreclr/pal/prebuilt/inc/xclrdata.h
index dbee62b9b1225..b28463dc86c78 100644
--- a/src/coreclr/pal/prebuilt/inc/xclrdata.h
+++ b/src/coreclr/pal/prebuilt/inc/xclrdata.h
@@ -3300,7 +3300,8 @@ enum __MIDL___MIDL_itf_xclrdata_0000_0008_0001
{
CLRDATA_MODULE_DEFAULT = 0,
CLRDATA_MODULE_IS_DYNAMIC = 0x1,
- CLRDATA_MODULE_IS_MEMORY_STREAM = 0x2
+ CLRDATA_MODULE_IS_MEMORY_STREAM = 0x2,
+ CLRDATA_MODULE_IS_MAIN_MODULE = 0x4
} CLRDataModuleFlag;
typedef /* [public][public][public] */
diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
index 46c53157b465a..f78e4df8a29e7 100644
--- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
@@ -1581,6 +1581,12 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream)
class AnsiStringMarshaller : Marshaller
{
+#if READYTORUN
+ const int MAX_LOCAL_BUFFER_LENGTH = 260 + 1; // MAX_PATH + 1
+
+ private ILLocalVariable? _localBuffer = null;
+#endif
+
internal override bool CleanupRequired
{
get
@@ -1605,12 +1611,75 @@ protected override void TransformManagedToNative(ILCodeStream codeStream)
#if READYTORUN
var stringToAnsi =
- Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler")
+ Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler")
.GetKnownMethod("ConvertToNative", null);
+
+ bool bPassByValueInOnly = In && !Out && !IsManagedByRef;
+
+ if (bPassByValueInOnly)
+ {
+ var bufSize = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
+ _localBuffer = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr));
+
+ // LocalBuffer = 0
+ codeStream.Emit(ILOpcode.ldnull);
+ codeStream.EmitStLoc((ILLocalVariable)_localBuffer);
+
+ var noOptimize = emitter.NewCodeLabel();
+
+ // if == NULL, goto NoOptimize
+ LoadManagedValue(codeStream);
+ codeStream.Emit(ILOpcode.brfalse, noOptimize);
+
+ // String.Length + 2
+ LoadManagedValue(codeStream);
+ var stringLen =
+ Context.GetWellKnownType(WellKnownType.String)
+ .GetKnownMethod("get_Length", null);
+ codeStream.Emit(ILOpcode.call, emitter.NewToken(stringLen));
+ codeStream.EmitLdc(2);
+ codeStream.Emit(ILOpcode.add);
+
+ // (String.Length + 2) * GetMaxDBCSCharByteSize()
+ codeStream.Emit(ILOpcode.ldsfld, emitter.NewToken(Context.SystemModule.GetKnownType(
+ "System.Runtime.InteropServices","Marshal")
+ .GetKnownField("SystemMaxDBCSCharSize")));
+ codeStream.Emit(ILOpcode.mul_ovf);
+
+ // BufSize = (String.Length + 2) * GetMaxDBCSCharByteSize()
+ codeStream.EmitStLoc(bufSize);
+
+ // if (MAX_LOCAL_BUFFER_LENGTH < BufSize ) goto NoOptimize
+ codeStream.EmitLdc(MAX_LOCAL_BUFFER_LENGTH + 1);
+ codeStream.EmitLdLoc(bufSize);
+ codeStream.Emit(ILOpcode.clt);
+ codeStream.Emit(ILOpcode.brtrue, noOptimize);
+
+ // LocalBuffer = localloc(BufSize);
+ codeStream.EmitLdLoc(bufSize);
+ codeStream.Emit(ILOpcode.localloc);
+ codeStream.EmitStLoc((ILLocalVariable)_localBuffer);
+
+ // NoOptimize:
+ codeStream.EmitLabel(noOptimize);
+ }
+
int flags = (PInvokeFlags.BestFitMapping ? 0x1 : 0)
| (PInvokeFlags.ThrowOnUnmappableChar ? 0x100 : 0);
+
+ // CSTRMarshaler.ConvertToNative pManaged, dwAnsiMarshalFlags, pLocalBuffer
codeStream.EmitLdc(flags);
LoadManagedValue(codeStream);
+
+ if (_localBuffer.HasValue)
+ {
+ codeStream.EmitLdLoc((ILLocalVariable)_localBuffer);
+ }
+ else
+ {
+ codeStream.Emit(ILOpcode.ldnull);
+ }
+
codeStream.Emit(ILOpcode.call, emitter.NewToken(stringToAnsi));
#else
LoadManagedValue(codeStream);
@@ -1631,7 +1700,7 @@ protected override void TransformNativeToManaged(ILCodeStream codeStream)
#if READYTORUN
var ansiToString =
- Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler")
+ Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler")
.GetKnownMethod("ConvertToManaged", null);
#else
var ansiToString = Context.GetHelperEntryPoint("InteropHelpers", "AnsiStringToString");
@@ -1645,11 +1714,28 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream)
{
var emitter = _ilCodeStreams.Emitter;
#if READYTORUN
+ var optimize = emitter.NewCodeLabel();
+
MethodDesc clearNative =
- Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler")
+ Context.SystemModule.GetKnownType("System.StubHelpers", "CSTRMarshaler")
.GetKnownMethod("ClearNative", null);
+
+ if (_localBuffer.HasValue)
+ {
+ // if (m_dwLocalBuffer) goto Optimize
+ codeStream.EmitLdLoc((ILLocalVariable)_localBuffer);
+ codeStream.Emit(ILOpcode.brtrue, optimize);
+ }
+
LoadNativeValue(codeStream);
+ // static void m_idClearNative(IntPtr ptr)
codeStream.Emit(ILOpcode.call, emitter.NewToken(clearNative));
+
+ // Optimize:
+ if (_localBuffer != default)
+ {
+ codeStream.EmitLabel(optimize);
+ }
#else
var lNullCheck = emitter.NewCodeLabel();
diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt
index 1ae433adbfd89..8c57742cb6315 100644
--- a/src/coreclr/utilcode/CMakeLists.txt
+++ b/src/coreclr/utilcode/CMakeLists.txt
@@ -69,6 +69,7 @@ endif(CLR_CMAKE_TARGET_WIN32)
set(UTILCODE_SOURCES
${UTILCODE_COMMON_SOURCES}
+ executableallocator.cpp
)
set(UTILCODE_DAC_SOURCES
diff --git a/src/coreclr/utilcode/executableallocator.cpp b/src/coreclr/utilcode/executableallocator.cpp
new file mode 100644
index 0000000000000..ac4c326c83784
--- /dev/null
+++ b/src/coreclr/utilcode/executableallocator.cpp
@@ -0,0 +1,755 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "pedecoder.h"
+#include "executableallocator.h"
+
+#if USE_UPPER_ADDRESS
+// Preferred region to allocate the code in.
+BYTE * ExecutableAllocator::g_codeMinAddr;
+BYTE * ExecutableAllocator::g_codeMaxAddr;
+BYTE * ExecutableAllocator::g_codeAllocStart;
+// Next address to try to allocate for code in the preferred region.
+BYTE * ExecutableAllocator::g_codeAllocHint;
+#endif // USE_UPPER_ADDRESS
+
+bool ExecutableAllocator::g_isWXorXEnabled = false;
+
+ExecutableAllocator::FatalErrorHandler ExecutableAllocator::g_fatalErrorHandler = NULL;
+
+ExecutableAllocator* ExecutableAllocator::g_instance = NULL;
+
+bool ExecutableAllocator::IsDoubleMappingEnabled()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ return false;
+#else
+ return g_isWXorXEnabled;
+#endif
+}
+
+bool ExecutableAllocator::IsWXORXEnabled()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ return true;
+#else
+ return g_isWXorXEnabled;
+#endif
+}
+
+extern SYSTEM_INFO g_SystemInfo;
+
+size_t ExecutableAllocator::Granularity()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return g_SystemInfo.dwAllocationGranularity;
+}
+
+// Use this function to initialize the g_codeAllocHint
+// during startup. base is runtime .dll base address,
+// size is runtime .dll virtual size.
+void ExecutableAllocator::InitCodeAllocHint(size_t base, size_t size, int randomPageOffset)
+{
+#if USE_UPPER_ADDRESS
+
+#ifdef _DEBUG
+ // If GetForceRelocs is enabled we don't constrain the pMinAddr
+ if (PEDecoder::GetForceRelocs())
+ return;
+#endif
+
+ //
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any code heap that doesn't specify an address
+ // range using [pMinAddr..pMaxAddr] we place it in the
+ // upper address space
+ // This enables us to avoid having to use long JumpStubs
+ // to reach the code for our ngen-ed images.
+ // Which are also placed in the UPPER_ADDRESS space.
+ //
+ SIZE_T reach = 0x7FFF0000u;
+
+ // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
+ // in clr.dll are the most heavily called functions.
+ g_codeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
+ g_codeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
+
+ BYTE * pStart;
+
+ if (g_codeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
+ (BYTE *)CODEHEAP_START_ADDRESS < g_codeMaxAddr)
+ {
+ // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
+ // Use the code head start address that does not cause collisions with NGen images.
+ // This logic is coupled with scripts that we use to assign base addresses.
+ pStart = (BYTE *)CODEHEAP_START_ADDRESS;
+ }
+ else
+ if (base > UINT32_MAX)
+ {
+ // clr.dll got address assigned by ASLR?
+ // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
+ // addresses. Do not start at g_codeMinAddr exactly so that we can also reach common native images
+ // that can be placed at higher addresses than clr.dll.
+ pStart = g_codeMinAddr + (g_codeMaxAddr - g_codeMinAddr) / 8;
+ }
+ else
+ {
+ // clr.dll missed the base address?
+ // Try to occupy the space right after it.
+ pStart = (BYTE *)(base + size);
+ }
+
+ // Randomize the address space
+ pStart += GetOsPageSize() * randomPageOffset;
+
+ g_codeAllocStart = pStart;
+ g_codeAllocHint = pStart;
+#endif
+}
+
+// Use this function to reset the g_codeAllocHint
+// after unloading an AppDomain
+void ExecutableAllocator::ResetCodeAllocHint()
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ g_codeAllocHint = g_codeAllocStart;
+#endif
+}
+
+// Returns TRUE if p is located in near clr.dll that allows us
+// to use rel32 IP-relative addressing modes.
+bool ExecutableAllocator::IsPreferredExecutableRange(void * p)
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ if (g_codeMinAddr <= (BYTE *)p && (BYTE *)p < g_codeMaxAddr)
+ return true;
+#endif
+ return false;
+}
+
+ExecutableAllocator* ExecutableAllocator::Instance()
+{
+ LIMITED_METHOD_CONTRACT;
+ return g_instance;
+}
+
+ExecutableAllocator::~ExecutableAllocator()
+{
+ if (IsDoubleMappingEnabled())
+ {
+ VMToOSInterface::DestroyDoubleMemoryMapper(m_doubleMemoryMapperHandle);
+ }
+}
+
+HRESULT ExecutableAllocator::StaticInitialize(FatalErrorHandler fatalErrorHandler)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ g_fatalErrorHandler = fatalErrorHandler;
+ g_isWXorXEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableWriteXorExecute) != 0;
+ g_instance = new (nothrow) ExecutableAllocator();
+ if (g_instance == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (!g_instance->Initialize())
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+bool ExecutableAllocator::Initialize()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (IsDoubleMappingEnabled())
+ {
+ if (!VMToOSInterface::CreateDoubleMemoryMapper(&m_doubleMemoryMapperHandle, &m_maxExecutableCodeSize))
+ {
+ return false;
+ }
+
+ m_CriticalSection = ClrCreateCriticalSection(CrstExecutableAllocatorLock,CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD));
+ }
+
+ return true;
+}
+
+//#define ENABLE_CACHED_MAPPINGS
+
+void ExecutableAllocator::UpdateCachedMapping(BlockRW* pBlock)
+{
+ LIMITED_METHOD_CONTRACT;
+#ifdef ENABLE_CACHED_MAPPINGS
+ if (m_cachedMapping == NULL)
+ {
+ m_cachedMapping = pBlock;
+ pBlock->refCount++;
+ }
+ else if (m_cachedMapping != pBlock)
+ {
+ void* unmapAddress = NULL;
+ size_t unmapSize;
+
+ if (!RemoveRWBlock(m_cachedMapping->baseRW, &unmapAddress, &unmapSize))
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found"));
+ }
+ if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize))
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed"));
+ }
+ m_cachedMapping = pBlock;
+ pBlock->refCount++;
+ }
+#endif // ENABLE_CACHED_MAPPINGS
+}
+
+void* ExecutableAllocator::FindRWBlock(void* baseRX, size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next)
+ {
+ if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size))
+ {
+ pBlock->refCount++;
+ UpdateCachedMapping(pBlock);
+
+ return (BYTE*)pBlock->baseRW + ((size_t)baseRX - (size_t)pBlock->baseRX);
+ }
+ }
+
+ return NULL;
+}
+
+bool ExecutableAllocator::AddRWBlock(void* baseRW, void* baseRX, size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ for (BlockRW* pBlock = m_pFirstBlockRW; pBlock != NULL; pBlock = pBlock->next)
+ {
+ if (pBlock->baseRX <= baseRX && ((size_t)baseRX + size) <= ((size_t)pBlock->baseRX + pBlock->size))
+ {
+ break;
+ }
+ }
+
+ // The new "nothrow" below failure is handled as fail fast since it is not recoverable
+ PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure);
+
+ BlockRW* pBlockRW = new (nothrow) BlockRW();
+ if (pBlockRW == NULL)
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block metadata cannot be allocated"));
+ return false;
+ }
+
+ pBlockRW->baseRW = baseRW;
+ pBlockRW->baseRX = baseRX;
+ pBlockRW->size = size;
+ pBlockRW->next = m_pFirstBlockRW;
+ pBlockRW->refCount = 1;
+ m_pFirstBlockRW = pBlockRW;
+
+ UpdateCachedMapping(pBlockRW);
+
+ return true;
+}
+
+bool ExecutableAllocator::RemoveRWBlock(void* pRW, void** pUnmapAddress, size_t* pUnmapSize)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ BlockRW* pPrevBlockRW = NULL;
+ for (BlockRW* pBlockRW = m_pFirstBlockRW; pBlockRW != NULL; pBlockRW = pBlockRW->next)
+ {
+ if (pBlockRW->baseRW <= pRW && (size_t)pRW < ((size_t)pBlockRW->baseRW + pBlockRW->size))
+ {
+ // found
+ pBlockRW->refCount--;
+ if (pBlockRW->refCount != 0)
+ {
+ *pUnmapAddress = NULL;
+ return true;
+ }
+
+ if (pPrevBlockRW == NULL)
+ {
+ m_pFirstBlockRW = pBlockRW->next;
+ }
+ else
+ {
+ pPrevBlockRW->next = pBlockRW->next;
+ }
+
+ *pUnmapAddress = pBlockRW->baseRW;
+ *pUnmapSize = pBlockRW->size;
+
+ delete pBlockRW;
+ return true;
+ }
+
+ pPrevBlockRW = pBlockRW;
+ }
+
+ return false;
+}
+
+bool ExecutableAllocator::AllocateOffset(size_t* pOffset, size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t offset = m_freeOffset;
+ size_t newFreeOffset = offset + size;
+
+ if (newFreeOffset > m_maxExecutableCodeSize)
+ {
+ return false;
+ }
+
+ m_freeOffset = newFreeOffset;
+
+ *pOffset = offset;
+
+ return true;
+}
+
+void ExecutableAllocator::AddRXBlock(BlockRX* pBlock)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ pBlock->next = m_pFirstBlockRX;
+ m_pFirstBlockRX = pBlock;
+}
+
+void* ExecutableAllocator::Commit(void* pStart, size_t size, bool isExecutable)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (IsDoubleMappingEnabled())
+ {
+ return VMToOSInterface::CommitDoubleMappedMemory(pStart, size, isExecutable);
+ }
+ else
+ {
+ return ClrVirtualAlloc(pStart, size, MEM_COMMIT, isExecutable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ }
+}
+
+void ExecutableAllocator::Release(void* pRX)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (IsDoubleMappingEnabled())
+ {
+ CRITSEC_Holder csh(m_CriticalSection);
+
+ // Locate the RX block corresponding to the pRX and remove it from the linked list
+ BlockRX* pBlock;
+ BlockRX* pPrevBlock = NULL;
+
+ for (pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next)
+ {
+ if (pRX == pBlock->baseRX)
+ {
+ if (pPrevBlock == NULL)
+ {
+ m_pFirstBlockRX = pBlock->next;
+ }
+ else
+ {
+ pPrevBlock->next = pBlock->next;
+ }
+
+ break;
+ }
+ pPrevBlock = pBlock;
+ }
+
+ if (pBlock != NULL)
+ {
+ VMToOSInterface::ReleaseDoubleMappedMemory(m_doubleMemoryMapperHandle, pRX, pBlock->offset, pBlock->size);
+ // Put the released block into the free block list
+ pBlock->baseRX = NULL;
+ pBlock->next = m_pFirstFreeBlockRX;
+ m_pFirstFreeBlockRX = pBlock;
+ }
+ else
+ {
+ // The block was not found, which should never happen.
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to release was not found"));
+ }
+ }
+ else
+ {
+ ClrVirtualFree(pRX, 0, MEM_RELEASE);
+ }
+}
+
+// Find a free block with the closest size >= the requested size.
+// Returns NULL if no such block exists.
+ExecutableAllocator::BlockRX* ExecutableAllocator::FindBestFreeBlock(size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ BlockRX* pPrevBlock = NULL;
+ BlockRX* pPrevBestBlock = NULL;
+ BlockRX* pBestBlock = NULL;
+ BlockRX* pBlock = m_pFirstFreeBlockRX;
+
+ while (pBlock != NULL)
+ {
+ if (pBlock->size >= size)
+ {
+ if (pBestBlock != NULL)
+ {
+ if (pBlock->size < pBestBlock->size)
+ {
+ pPrevBestBlock = pPrevBlock;
+ pBestBlock = pBlock;
+ }
+ }
+ else
+ {
+ pPrevBestBlock = pPrevBlock;
+ pBestBlock = pBlock;
+ }
+ }
+ pPrevBlock = pBlock;
+ pBlock = pBlock->next;
+ }
+
+ if (pBestBlock != NULL)
+ {
+ if (pPrevBestBlock != NULL)
+ {
+ pPrevBestBlock->next = pBestBlock->next;
+ }
+ else
+ {
+ m_pFirstFreeBlockRX = pBestBlock->next;
+ }
+
+ pBestBlock->next = NULL;
+ }
+
+ return pBestBlock;
+}
+
+// Allocate a new block of executable memory and the related descriptor structure.
+// First try to get it from the free blocks and if there is no suitable free block,
+// allocate a new one.
+ExecutableAllocator::BlockRX* ExecutableAllocator::AllocateBlock(size_t size, bool* pIsFreeBlock)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t offset;
+ BlockRX* block = FindBestFreeBlock(size);
+ *pIsFreeBlock = (block != NULL);
+
+ if (block == NULL)
+ {
+ if (!AllocateOffset(&offset, size))
+ {
+ return NULL;
+ }
+
+ block = new (nothrow) BlockRX();
+ if (block == NULL)
+ {
+ return NULL;
+ }
+
+ block->offset = offset;
+ block->size = size;
+ }
+
+ return block;
+}
+
+// Backout a previously allocated block. The block is added to the free blocks list and
+// reused for later allocation requests.
+void ExecutableAllocator::BackoutBlock(BlockRX* pBlock, bool isFreeBlock)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!isFreeBlock)
+ {
+ m_freeOffset -= pBlock->size;
+ delete pBlock;
+ }
+ else
+ {
+ pBlock->next = m_pFirstFreeBlockRX;
+ m_pFirstFreeBlockRX = pBlock;
+ }
+}
+
+// Reserve executable memory within the specified virtual address space range. If it is not possible to
+// reserve memory in that range, the method returns NULL and nothing is allocated.
+void* ExecutableAllocator::ReserveWithinRange(size_t size, const void* loAddress, const void* hiAddress)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE((size & (Granularity() - 1)) == 0);
+ if (IsDoubleMappingEnabled())
+ {
+ CRITSEC_Holder csh(m_CriticalSection);
+
+ bool isFreeBlock;
+ BlockRX* block = AllocateBlock(size, &isFreeBlock);
+ if (block == NULL)
+ {
+ return NULL;
+ }
+
+ void *result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, loAddress, hiAddress);
+
+ if (result != NULL)
+ {
+ block->baseRX = result;
+ AddRXBlock(block);
+ }
+ else
+ {
+ BackoutBlock(block, isFreeBlock);
+ }
+
+ return result;
+ }
+ else
+ {
+ DWORD allocationType = MEM_RESERVE;
+#ifdef HOST_UNIX
+ // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
+ // This will allow us to place JIT'ed code close to the coreclr library
+ // and thus improve performance by avoiding jump stubs in managed code.
+ allocationType |= MEM_RESERVE_EXECUTABLE;
+#endif
+ return ClrVirtualAllocWithinRange((const BYTE*)loAddress, (const BYTE*)hiAddress, size, allocationType, PAGE_NOACCESS);
+ }
+}
+
+// Reserve executable memory. On Windows it tries to use the allocation hints to
+// allocate memory close to the previously allocated executable memory and loaded
+// executable files.
+void* ExecutableAllocator::Reserve(size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE((size & (Granularity() - 1)) == 0);
+
+ BYTE *result = NULL;
+
+#if USE_UPPER_ADDRESS
+ //
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any heap that will contain executable code
+ // we will place it in the upper address space
+ //
+ // This enables us to avoid having to use JumpStubs
+ // to reach the code for our ngen-ed images on x64,
+ // since they are also placed in the UPPER_ADDRESS space.
+ //
+ BYTE * pHint = g_codeAllocHint;
+
+ if (size <= (SIZE_T)(g_codeMaxAddr - g_codeMinAddr) && pHint != NULL)
+ {
+ // Try to allocate in the preferred region after the hint
+ result = (BYTE*)ReserveWithinRange(size, pHint, g_codeMaxAddr);
+ if (result != NULL)
+ {
+ g_codeAllocHint = result + size;
+ }
+ else
+ {
+ // Try to allocate in the preferred region before the hint
+ result = (BYTE*)ReserveWithinRange(size, g_codeMinAddr, pHint + size);
+
+ if (result != NULL)
+ {
+ g_codeAllocHint = result + size;
+ }
+
+ g_codeAllocHint = NULL;
+ }
+ }
+
+ // Fall through to
+#endif // USE_UPPER_ADDRESS
+
+ if (result == NULL)
+ {
+ if (IsDoubleMappingEnabled())
+ {
+ CRITSEC_Holder csh(m_CriticalSection);
+
+ bool isFreeBlock;
+ BlockRX* block = AllocateBlock(size, &isFreeBlock);
+ if (block == NULL)
+ {
+ return NULL;
+ }
+
+ result = (BYTE*)VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, 0, 0);
+
+ if (result != NULL)
+ {
+ block->baseRX = result;
+ AddRXBlock(block);
+ }
+ else
+ {
+ BackoutBlock(block, isFreeBlock);
+ }
+ }
+ else
+ {
+ DWORD allocationType = MEM_RESERVE;
+#ifdef HOST_UNIX
+ // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
+ // This will allow us to place JIT'ed code close to the coreclr library
+ // and thus improve performance by avoiding jump stubs in managed code.
+ allocationType |= MEM_RESERVE_EXECUTABLE;
+#endif
+ result = (BYTE*)ClrVirtualAlloc(NULL, size, allocationType, PAGE_NOACCESS);
+ }
+ }
+
+ return result;
+}
+
+// Reserve a block of executable memory at the specified virtual address. If it is not
+// possible, the method returns NULL.
+void* ExecutableAllocator::ReserveAt(void* baseAddressRX, size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE((size & (Granularity() - 1)) == 0);
+
+ if (IsDoubleMappingEnabled())
+ {
+ CRITSEC_Holder csh(m_CriticalSection);
+
+ bool isFreeBlock;
+ BlockRX* block = AllocateBlock(size, &isFreeBlock);
+ if (block == NULL)
+ {
+ return NULL;
+ }
+
+ void* result = VMToOSInterface::ReserveDoubleMappedMemory(m_doubleMemoryMapperHandle, block->offset, size, baseAddressRX, baseAddressRX);
+
+ if (result != NULL)
+ {
+ block->baseRX = result;
+ AddRXBlock(block);
+ }
+ else
+ {
+ BackoutBlock(block, isFreeBlock);
+ }
+
+ return result;
+ }
+ else
+ {
+ return VirtualAlloc(baseAddressRX, size, MEM_RESERVE, PAGE_NOACCESS);
+ }
+}
+
+// Map an executable memory block as writeable. If there is already a mapping
+// covering the specified block, return that mapping instead of creating a new one.
+// Return starting address of the writeable mapping.
+void* ExecutableAllocator::MapRW(void* pRX, size_t size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!IsDoubleMappingEnabled())
+ {
+ return pRX;
+ }
+
+ CRITSEC_Holder csh(m_CriticalSection);
+
+ void* result = FindRWBlock(pRX, size);
+ if (result != NULL)
+ {
+ return result;
+ }
+
+ for (BlockRX* pBlock = m_pFirstBlockRX; pBlock != NULL; pBlock = pBlock->next)
+ {
+ if (pRX >= pBlock->baseRX && ((size_t)pRX + size) <= ((size_t)pBlock->baseRX + pBlock->size))
+ {
+ // Offset of the RX address in the originally allocated block
+ size_t offset = (size_t)pRX - (size_t)pBlock->baseRX;
+ // Offset of the RX address that will start the newly mapped block
+ size_t mapOffset = ALIGN_DOWN(offset, Granularity());
+ // Size of the block we will map
+ size_t mapSize = ALIGN_UP(offset - mapOffset + size, Granularity());
+ void* pRW = VMToOSInterface::GetRWMapping(m_doubleMemoryMapperHandle, (BYTE*)pBlock->baseRX + mapOffset, pBlock->offset + mapOffset, mapSize);
+
+ if (pRW == NULL)
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Failed to create RW mapping for RX memory"));
+ }
+
+ AddRWBlock(pRW, (BYTE*)pBlock->baseRX + mapOffset, mapSize);
+
+ return (void*)((size_t)pRW + (offset - mapOffset));
+ }
+ else if (pRX >= pBlock->baseRX && pRX < (void*)((size_t)pBlock->baseRX + pBlock->size))
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to RW map a block that crosses the end of the allocated RX range"));
+ }
+ else if (pRX < pBlock->baseRX && (void*)((size_t)pRX + size) > pBlock->baseRX)
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Attempting to map a block that crosses the beginning of the allocated range"));
+ }
+ }
+
+ // The executable memory block was not found, so we cannot provide the writeable mapping.
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RX block to map as RW was not found"));
+ return NULL;
+}
+
+// Unmap writeable mapping at the specified address. The address must be an address
+// returned by the MapRW method.
+void ExecutableAllocator::UnmapRW(void* pRW)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!IsDoubleMappingEnabled())
+ {
+ return;
+ }
+
+ CRITSEC_Holder csh(m_CriticalSection);
+ _ASSERTE(pRW != NULL);
+
+ void* unmapAddress = NULL;
+ size_t unmapSize;
+
+ if (!RemoveRWBlock(pRW, &unmapAddress, &unmapSize))
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("The RW block to unmap was not found"));
+ }
+
+ if (unmapAddress && !VMToOSInterface::ReleaseRWMapping(unmapAddress, unmapSize))
+ {
+ g_fatalErrorHandler(COR_E_EXECUTIONENGINE, W("Releasing the RW mapping failed"));
+ }
+}
diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp
index adaf07d8f5825..b3b381b2f9bef 100644
--- a/src/coreclr/utilcode/loaderheap.cpp
+++ b/src/coreclr/utilcode/loaderheap.cpp
@@ -695,15 +695,21 @@ size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap);
struct LoaderHeapFreeBlock
{
public:
- LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list
- size_t m_dwSize; // Total size of this block (including this header)
-//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations.
+ LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list
+ size_t m_dwSize; // Total size of this block
+ void *m_pBlockAddress; // Virtual address of the block
+#ifndef DACCESS_COMPILE
static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
+ // The new "nothrow" below failure is handled in a non-fault way, so
+ // make sure that callers with FORBID_FAULT can call this method without
+ // firing the contract violation assert.
+ PERMANENT_CONTRACT_VIOLATION(FaultViolation, ReasonContractInfrastructure);
+
LOADER_HEAP_BEGIN_TRAP_FAULT
// It's illegal to insert a free block that's smaller than the minimum sized allocation -
@@ -722,19 +728,30 @@ struct LoaderHeapFreeBlock
}
#endif
- INDEBUG(memset(pMem, 0xcc, dwTotalSize);)
- LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem;
- pNewBlock->m_pNext = *ppHead;
- pNewBlock->m_dwSize = dwTotalSize;
- *ppHead = pNewBlock;
+ void* pMemRW = pMem;
+ ExecutableWriterHolder memWriterHolder;
+ if (pHeap->IsExecutable())
+ {
+ memWriterHolder = ExecutableWriterHolder(pMem, dwTotalSize);
+ pMemRW = memWriterHolder.GetRW();
+ }
- MergeBlock(pNewBlock, pHeap);
+ INDEBUG(memset(pMemRW, 0xcc, dwTotalSize);)
+ LoaderHeapFreeBlock *pNewBlock = new (nothrow) LoaderHeapFreeBlock;
+ // If we fail allocating the LoaderHeapFreeBlock, ignore the failure and don't insert the free block at all.
+ if (pNewBlock != NULL)
+ {
+ pNewBlock->m_pNext = *ppHead;
+ pNewBlock->m_dwSize = dwTotalSize;
+ pNewBlock->m_pBlockAddress = pMem;
+ *ppHead = pNewBlock;
+ MergeBlock(pNewBlock, pHeap);
+ }
LOADER_HEAP_END_TRAP_FAULT
}
-
- static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap)
+ static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, UnlockedLoaderHeap *pHeap)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
@@ -751,23 +768,19 @@ struct LoaderHeapFreeBlock
size_t dwCurSize = pCur->m_dwSize;
if (dwCurSize == dwSize)
{
- pResult = pCur;
+ pResult = pCur->m_pBlockAddress;
// Exact match. Hooray!
- if (fRemoveFromFreeList)
- {
- *ppWalk = pCur->m_pNext;
- }
+ *ppWalk = pCur->m_pNext;
+ delete pCur;
break;
}
else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap))
{
// Partial match. Ok...
- pResult = pCur;
- if (fRemoveFromFreeList)
- {
- *ppWalk = pCur->m_pNext;
- InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap );
- }
+ pResult = pCur->m_pBlockAddress;
+ *ppWalk = pCur->m_pNext;
+ InsertFreeBlock(ppWalk, ((BYTE*)pCur->m_pBlockAddress) + dwSize, dwCurSize - dwSize, pHeap );
+ delete pCur;
break;
}
@@ -777,19 +790,22 @@ struct LoaderHeapFreeBlock
ppWalk = &( pCur->m_pNext );
}
- if (pResult && fRemoveFromFreeList)
+ if (pResult)
{
+ void *pResultRW = pResult;
+ ExecutableWriterHolder resultWriterHolder;
+ if (pHeap->IsExecutable())
+ {
+ resultWriterHolder = ExecutableWriterHolder(pResult, dwSize);
+ pResultRW = resultWriterHolder.GetRW();
+ }
// Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant!
- memset(pResult, 0, dwSize);
+ memset(pResultRW, 0, dwSize);
}
LOADER_HEAP_END_TRAP_FAULT
return pResult;
-
-
-
}
-
private:
// Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened.
static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap)
@@ -803,7 +819,7 @@ struct LoaderHeapFreeBlock
LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext;
size_t dwSize = pFreeBlock->m_dwSize;
- if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize))
+ if (pNextBlock == NULL || ((BYTE*)pNextBlock->m_pBlockAddress) != (((BYTE*)pFreeBlock->m_pBlockAddress) + dwSize))
{
result = FALSE;
}
@@ -811,9 +827,17 @@ struct LoaderHeapFreeBlock
{
size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize;
LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext;
- INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);)
+ void *pMemRW = pFreeBlock->m_pBlockAddress;
+ ExecutableWriterHolder memWriterHolder;
+ if (pHeap->IsExecutable())
+ {
+ memWriterHolder = ExecutableWriterHolder(pFreeBlock->m_pBlockAddress, dwCombinedSize);
+ pMemRW = memWriterHolder.GetRW();
+ }
+ INDEBUG(memset(pMemRW, 0xcc, dwCombinedSize);)
pFreeBlock->m_pNext = pNextNextBlock;
pFreeBlock->m_dwSize = dwCombinedSize;
+ delete pNextBlock;
result = TRUE;
}
@@ -822,7 +846,7 @@ struct LoaderHeapFreeBlock
return result;
}
-
+#endif // DACCESS_COMPILE
};
@@ -840,8 +864,7 @@ struct LoaderHeapFreeBlock
// - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte)
// - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag)
//
-// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock)
-// - c bytes of pad (where "c" is just enough to pointer-align the following byte)
+// - b bytes of pad (where "b" is just enough to pointer-align the following byte)
//
// ==> Following address is always pointer-aligned
//=====================================================================================
@@ -862,10 +885,6 @@ inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHe
#ifdef _DEBUG
dwSize += sizeof(LoaderHeapValidationTag);
#endif
- if (dwSize < sizeof(LoaderHeapFreeBlock))
- {
- dwSize = sizeof(LoaderHeapFreeBlock);
- }
}
dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
@@ -977,9 +996,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap()
if (fReleaseMemory)
{
- BOOL fSuccess;
- fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE);
- _ASSERTE(fSuccess);
+ ExecutableAllocator::Instance()->Release(pVirtualAddress);
}
delete pSearch;
@@ -987,9 +1004,7 @@ UnlockedLoaderHeap::~UnlockedLoaderHeap()
if (m_reservedBlock.m_fReleaseMemory)
{
- BOOL fSuccess;
- fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE);
- _ASSERTE(fSuccess);
+ ExecutableAllocator::Instance()->Release(m_reservedBlock.pVirtualAddress);
}
INDEBUG(s_dwNumInstancesOfLoaderHeaps --;)
@@ -1058,7 +1073,7 @@ void ReleaseReservedMemory(BYTE* value)
{
if (value)
{
- ClrVirtualFree(value, 0, MEM_RELEASE);
+ ExecutableAllocator::Instance()->Release(value);
}
}
@@ -1114,7 +1129,9 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
// Reserve pages
//
- pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS);
+ // Reserve the memory for even non-executable stuff close to the executable code, as it has profound effect
+ // on e.g. a static variable access performance.
+ pData = (BYTE *)ExecutableAllocator::Instance()->Reserve(dwSizeToReserve);
if (pData == NULL)
{
return FALSE;
@@ -1140,7 +1157,7 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
}
// Commit first set of pages, since it will contain the LoaderHeapBlock
- void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ void *pTemp = ExecutableAllocator::Instance()->Commit(pData, dwSizeToCommit, (m_Options & LHF_EXECUTABLE));
if (pTemp == NULL)
{
//_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap");
@@ -1213,7 +1230,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize)
dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize());
// Yes, so commit the desired number of reserved pages
- void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ void *pData = ExecutableAllocator::Instance()->Commit(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, (m_Options & LHF_EXECUTABLE));
if (pData == NULL)
return FALSE;
@@ -1316,7 +1333,7 @@ void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize
{
// Any memory available on the free list?
- void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this);
+ void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, this);
if (!pData)
{
// Enough bytes available in committed region?
@@ -1518,8 +1535,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize ))
{
- // Cool. This was the last block allocated. We can just undo the allocation instead
- // of going to the freelist.
void *pMemRW = pMem;
ExecutableWriterHolder memWriterHolder;
if (m_Options & LHF_EXECUTABLE)
@@ -1527,6 +1542,9 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
memWriterHolder = ExecutableWriterHolder(pMem, dwSize);
pMemRW = memWriterHolder.GetRW();
}
+
+ // Cool. This was the last block allocated. We can just undo the allocation instead
+ // of going to the freelist.
memset(pMemRW, 0x00, dwSize); // Fill freed region with 0
m_pAllocPtr = (BYTE*)pMem;
}
@@ -1534,7 +1552,6 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
{
LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this);
}
-
}
diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp
index 0026d1f619f14..e7b1755b2b1c4 100644
--- a/src/coreclr/utilcode/util.cpp
+++ b/src/coreclr/utilcode/util.cpp
@@ -352,168 +352,6 @@ HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
return hr;
}
-#if USE_UPPER_ADDRESS
-static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
-static BYTE * s_CodeMaxAddr;
-static BYTE * s_CodeAllocStart;
-static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region.
-#endif
-
-//
-// Use this function to initialize the s_CodeAllocHint
-// during startup. base is runtime .dll base address,
-// size is runtime .dll virtual size.
-//
-void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset)
-{
-#if USE_UPPER_ADDRESS
-
-#ifdef _DEBUG
- // If GetForceRelocs is enabled we don't constrain the pMinAddr
- if (PEDecoder::GetForceRelocs())
- return;
-#endif
-
-//
- // If we are using the UPPER_ADDRESS space (on Win64)
- // then for any code heap that doesn't specify an address
- // range using [pMinAddr..pMaxAddr] we place it in the
- // upper address space
- // This enables us to avoid having to use long JumpStubs
- // to reach the code for our ngen-ed images.
- // Which are also placed in the UPPER_ADDRESS space.
- //
- SIZE_T reach = 0x7FFF0000u;
-
- // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
- // in clr.dll are the most heavily called functions.
- s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
- s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
-
- BYTE * pStart;
-
- if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
- (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr)
- {
- // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
- // Use the code head start address that does not cause collisions with NGen images.
- // This logic is coupled with scripts that we use to assign base addresses.
- pStart = (BYTE *)CODEHEAP_START_ADDRESS;
- }
- else
- if (base > UINT32_MAX)
- {
- // clr.dll got address assigned by ASLR?
- // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
- // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images
- // that can be placed at higher addresses than clr.dll.
- pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8;
- }
- else
- {
- // clr.dll missed the base address?
- // Try to occupy the space right after it.
- pStart = (BYTE *)(base + size);
- }
-
- // Randomize the address space
- pStart += GetOsPageSize() * randomPageOffset;
-
- s_CodeAllocStart = pStart;
- s_CodeAllocHint = pStart;
-#endif
-}
-
-//
-// Use this function to reset the s_CodeAllocHint
-// after unloading an AppDomain
-//
-void ResetCodeAllocHint()
-{
- LIMITED_METHOD_CONTRACT;
-#if USE_UPPER_ADDRESS
- s_CodeAllocHint = s_CodeAllocStart;
-#endif
-}
-
-//
-// Returns TRUE if p is located in near clr.dll that allows us
-// to use rel32 IP-relative addressing modes.
-//
-BOOL IsPreferredExecutableRange(void * p)
-{
- LIMITED_METHOD_CONTRACT;
-#if USE_UPPER_ADDRESS
- if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr)
- return TRUE;
-#endif
- return FALSE;
-}
-
-//
-// Allocate free memory that will be used for executable code
-// Handles the special requirements that we have on 64-bit platforms
-// where we want the executable memory to be located near clr.dll
-//
-BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
- DWORD flAllocationType,
- DWORD flProtect)
-{
- CONTRACTL
- {
- NOTHROW;
- }
- CONTRACTL_END;
-
-#if USE_UPPER_ADDRESS
- //
- // If we are using the UPPER_ADDRESS space (on Win64)
- // then for any heap that will contain executable code
- // we will place it in the upper address space
- //
- // This enables us to avoid having to use JumpStubs
- // to reach the code for our ngen-ed images on x64,
- // since they are also placed in the UPPER_ADDRESS space.
- //
- BYTE * pHint = s_CodeAllocHint;
-
- if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL)
- {
- // Try to allocate in the preferred region after the hint
- BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect);
-
- if (pResult != NULL)
- {
- s_CodeAllocHint = pResult + dwSize;
- return pResult;
- }
-
- // Try to allocate in the preferred region before the hint
- pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect);
-
- if (pResult != NULL)
- {
- s_CodeAllocHint = pResult + dwSize;
- return pResult;
- }
-
- s_CodeAllocHint = NULL;
- }
-
- // Fall through to
-#endif // USE_UPPER_ADDRESS
-
-#ifdef HOST_UNIX
- // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
- // This will allow us to place JIT'ed code close to the coreclr library
- // and thus improve performance by avoiding jump stubs in managed code.
- flAllocationType |= MEM_RESERVE_EXECUTABLE;
-#endif // HOST_UNIX
-
- return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect);
-
-}
-
//
// Allocate free memory with specific alignment.
//
diff --git a/src/coreclr/utilcode/yieldprocessornormalized.cpp b/src/coreclr/utilcode/yieldprocessornormalized.cpp
index 4242f82792b47..020d8d7cc79e4 100644
--- a/src/coreclr/utilcode/yieldprocessornormalized.cpp
+++ b/src/coreclr/utilcode/yieldprocessornormalized.cpp
@@ -2,8 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
#include "stdafx.h"
+#include "yieldprocessornormalized.h"
-// Defaults are for when InitializeYieldProcessorNormalized has not yet been called or when no measurement is done, and are
-// tuned for Skylake processors
-unsigned int g_yieldsPerNormalizedYield = 1; // current value is for Skylake processors, this is expected to be ~8 for pre-Skylake
-unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration = 7;
+bool YieldProcessorNormalization::s_isMeasurementScheduled;
+
+// Defaults are for when normalization has not yet been done
+unsigned int YieldProcessorNormalization::s_yieldsPerNormalizedYield = 1;
+unsigned int YieldProcessorNormalization::s_optimalMaxNormalizedYieldsPerSpinIteration =
+ (unsigned int)
+ (
+ (double)YieldProcessorNormalization::TargetMaxNsPerSpinIteration /
+ YieldProcessorNormalization::TargetNsPerNormalizedYield +
+ 0.5
+ );
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 1d682d2a428bb..f31e5a3ca12a6 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -136,7 +136,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
versionresilienthashcode.cpp
virtualcallstub.cpp
win32threadpool.cpp
- yieldprocessornormalized.cpp
zapsig.cpp
)
@@ -389,6 +388,7 @@ set(VM_SOURCES_WKS
threadsuspend.cpp
typeparse.cpp
weakreferencenative.cpp
+ yieldprocessornormalized.cpp
${VM_SOURCES_GDBJIT}
)
@@ -833,7 +833,6 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM)
set(VM_SOURCES_DAC_AND_WKS_ARCH
${ARCH_SOURCES_DIR}/exceparm.cpp
${ARCH_SOURCES_DIR}/stubs.cpp
- ${ARCH_SOURCES_DIR}/armsinglestepper.cpp
)
set(VM_HEADERS_DAC_AND_WKS_ARCH
@@ -844,6 +843,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM)
set(VM_SOURCES_WKS_ARCH
${ARCH_SOURCES_DIR}/profiler.cpp
+ ${ARCH_SOURCES_DIR}/armsinglestepper.cpp
exceptionhandling.cpp
gcinfodecoder.cpp
)
@@ -868,7 +868,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
)
if(CLR_CMAKE_HOST_UNIX)
- list(APPEND VM_SOURCES_DAC_AND_WKS_ARCH
+ list(APPEND VM_SOURCES_WKS_ARCH
${ARCH_SOURCES_DIR}/arm64singlestepper.cpp
)
endif(CLR_CMAKE_HOST_UNIX)
diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man
index 45895f16fce48..0eed049c17ba4 100644
--- a/src/coreclr/vm/ClrEtwAll.man
+++ b/src/coreclr/vm/ClrEtwAll.man
@@ -438,7 +438,13 @@
-
+
+
+
+
+
@@ -2916,6 +2922,19 @@
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
@@ -3313,6 +3332,10 @@
keywords ="ThreadingKeyword" opcode="Wait"
task="ThreadPoolWorkerThread"
symbol="ThreadPoolWorkerThreadWait" message="$(string.RuntimePublisher.ThreadPoolWorkerThreadEventMessage)"/>
+
+
+
@@ -8334,6 +8358,7 @@
+
diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst
index 4ac4fe405d9da..9c5738ef43dbc 100644
--- a/src/coreclr/vm/ClrEtwAllMeta.lst
+++ b/src/coreclr/vm/ClrEtwAllMeta.lst
@@ -134,9 +134,9 @@ nomac:GarbageCollection:::GCJoin_V2
nostack:Type:::BulkType
-###################
-# Threadpool events
-###################
+#################################
+# Threading and Threadpool events
+#################################
nomac:WorkerThreadCreation:::WorkerThreadCreate
noclrinstanceid:WorkerThreadCreation:::WorkerThreadCreate
nomac:WorkerThreadCreation:::WorkerThreadTerminate
@@ -170,6 +170,8 @@ nomac:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentSample
nostack:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentSample
nomac:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentAdjustment
nostack:ThreadPoolWorkerThreadAdjustment:::ThreadPoolWorkerThreadAdjustmentAdjustment
+nomac:YieldProcessorMeasurement:::YieldProcessorMeasurement
+nostack:YieldProcessorMeasurement:::YieldProcessorMeasurement
##################
# Exception events
diff --git a/src/coreclr/vm/amd64/JitHelpers_Fast.asm b/src/coreclr/vm/amd64/JitHelpers_Fast.asm
index 82a301bb0cbd1..219597eb350c2 100644
--- a/src/coreclr/vm/amd64/JitHelpers_Fast.asm
+++ b/src/coreclr/vm/amd64/JitHelpers_Fast.asm
@@ -51,37 +51,6 @@ endif
extern JIT_InternalThrow:proc
-; There is an even more optimized version of these helpers possible which takes
-; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2
-; that check (this is more significant in the JIT_WriteBarrier case).
-;
-; Additionally we can look into providing helpers which will take the src/dest from
-; specific registers (like x86) which _could_ (??) make for easier register allocation
-; for the JIT64, however it might lead to having to have some nasty code that treats
-; these guys really special like... :(.
-;
-; Version that does the move, checks whether or not it's in the GC and whether or not
-; it needs to have it's card updated
-;
-; void JIT_CheckedWriteBarrier(Object** dst, Object* src)
-LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
-
- ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
- ; but if it isn't then it will just return.
- ;
- ; See if this is in GCHeap
- cmp rcx, [g_lowest_address]
- jb NotInHeap
- cmp rcx, [g_highest_address]
- jnb NotInHeap
-
- jmp JIT_WriteBarrier
-
- NotInHeap:
- ; See comment above about possible AV
- mov [rcx], rdx
- ret
-LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT
; Mark start of the code region that we patch at runtime
LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
@@ -99,7 +68,8 @@ LEAF_ENTRY JIT_WriteBarrier, _TEXT
ifdef _DEBUG
; In debug builds, this just contains jump to the debug version of the write barrier by default
- jmp JIT_WriteBarrier_Debug
+ mov rax, JIT_WriteBarrier_Debug
+ jmp rax
endif
ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
@@ -388,6 +358,51 @@ endif
ret
LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT
+Section segment para 'DATA'
+
+ align 16
+
+ public JIT_WriteBarrier_Loc
+JIT_WriteBarrier_Loc:
+ dq 0
+
+LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
+ ; JIT_WriteBarrier(Object** dst, Object* src)
+ jmp QWORD PTR [JIT_WriteBarrier_Loc]
+LEAF_END JIT_WriteBarrier_Callable, _TEXT
+
+; There is an even more optimized version of these helpers possible which takes
+; advantage of knowledge of which way the ephemeral heap is growing to only do 1/2
+; that check (this is more significant in the JIT_WriteBarrier case).
+;
+; Additionally we can look into providing helpers which will take the src/dest from
+; specific registers (like x86) which _could_ (??) make for easier register allocation
+; for the JIT64, however it might lead to having to have some nasty code that treats
+; these guys really special like... :(.
+;
+; Version that does the move, checks whether or not it's in the GC and whether or not
+; it needs to have it's card updated
+;
+; void JIT_CheckedWriteBarrier(Object** dst, Object* src)
+LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
+
+ ; When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference
+ ; but if it isn't then it will just return.
+ ;
+ ; See if this is in GCHeap
+ cmp rcx, [g_lowest_address]
+ jb NotInHeap
+ cmp rcx, [g_highest_address]
+ jnb NotInHeap
+
+ jmp QWORD PTR [JIT_WriteBarrier_Loc]
+
+ NotInHeap:
+ ; See comment above about possible AV
+ mov [rcx], rdx
+ ret
+LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT
+
; The following helper will access ("probe") a word on each page of the stack
; starting with the page right beneath rsp down to the one pointed to by r11.
; The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame.
diff --git a/src/coreclr/vm/amd64/jithelpers_fast.S b/src/coreclr/vm/amd64/jithelpers_fast.S
index a13afb4878511..8109886d0c969 100644
--- a/src/coreclr/vm/amd64/jithelpers_fast.S
+++ b/src/coreclr/vm/amd64/jithelpers_fast.S
@@ -32,26 +32,14 @@ LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT
// See if this is in GCHeap
PREPARE_EXTERNAL_VAR g_lowest_address, rax
cmp rdi, [rax]
-#ifdef FEATURE_WRITEBARRIER_COPY
// jb NotInHeap
.byte 0x72, 0x12
-#else
- // jb NotInHeap
- .byte 0x72, 0x0e
-#endif
PREPARE_EXTERNAL_VAR g_highest_address, rax
cmp rdi, [rax]
-#ifdef FEATURE_WRITEBARRIER_COPY
// jnb NotInHeap
.byte 0x73, 0x06
jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)]
-#else
- // jnb NotInHeap
- .byte 0x73, 0x02
- // jmp C_FUNC(JIT_WriteBarrier)
- .byte 0xeb, 0x05
-#endif
NotInHeap:
// See comment above about possible AV
@@ -398,11 +386,17 @@ LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT
ret
LEAF_END_MARKED JIT_ByRefWriteBarrier, _TEXT
-#ifdef FEATURE_WRITEBARRIER_COPY
// When JIT_WriteBarrier is copied into an allocated page,
// helpers use this global variable to jump to it. This variable is set in InitThreadManager.
- .global _JIT_WriteBarrier_Loc
- .zerofill __DATA,__common,_JIT_WriteBarrier_Loc,8,3
+ .global C_FUNC(JIT_WriteBarrier_Loc)
+#ifdef TARGET_OSX
+ .zerofill __DATA,__common,C_FUNC(JIT_WriteBarrier_Loc),8,3
+#else
+ .data
+ C_FUNC(JIT_WriteBarrier_Loc):
+ .quad 0
+ .text
+#endif
// ------------------------------------------------------------------
// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
@@ -412,8 +406,6 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
jmp [rip + C_FUNC(JIT_WriteBarrier_Loc)]
LEAF_END JIT_WriteBarrier_Callable, _TEXT
-#endif // FEATURE_WRITEBARRIER_COPY
-
// The following helper will access ("probe") a word on each page of the stack
// starting with the page right beneath rsp down to the one pointed to by r11.
diff --git a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp
index 38bff78a54cb0..02b023777b8a9 100644
--- a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp
+++ b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp
@@ -293,7 +293,10 @@ int WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier,
// the memcpy must come before the switch statment because the asserts inside the switch
// are actually looking into the JIT_WriteBarrier buffer
- memcpy(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize());
+ {
+ ExecutableWriterHolder writeBarrierWriterHolder(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), GetCurrentWriteBarrierSize());
+ memcpy(writeBarrierWriterHolder.GetRW(), (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize());
+ }
switch (newWriteBarrier)
{
@@ -544,7 +547,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended)
// Change immediate if different from new g_ephermeral_high.
if (*(UINT64*)m_pUpperBoundImmediate != (size_t)g_ephemeral_high)
{
- *(UINT64*)m_pUpperBoundImmediate = (size_t)g_ephemeral_high;
+ ExecutableWriterHolder upperBoundWriterHolder((UINT64*)m_pUpperBoundImmediate, sizeof(UINT64));
+ *upperBoundWriterHolder.GetRW() = (size_t)g_ephemeral_high;
stompWBCompleteActions |= SWB_ICACHE_FLUSH;
}
}
@@ -557,7 +561,8 @@ int WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended)
// Change immediate if different from new g_ephermeral_low.
if (*(UINT64*)m_pLowerBoundImmediate != (size_t)g_ephemeral_low)
{
- *(UINT64*)m_pLowerBoundImmediate = (size_t)g_ephemeral_low;
+ ExecutableWriterHolder lowerBoundImmediateWriterHolder((UINT64*)m_pLowerBoundImmediate, sizeof(UINT64));
+ *lowerBoundImmediateWriterHolder.GetRW() = (size_t)g_ephemeral_low;
stompWBCompleteActions |= SWB_ICACHE_FLUSH;
}
break;
@@ -609,7 +614,8 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus
#endif // FEATURE_SVR_GC
if (*(UINT64*)m_pWriteWatchTableImmediate != (size_t)g_sw_ww_table)
{
- *(UINT64*)m_pWriteWatchTableImmediate = (size_t)g_sw_ww_table;
+ ExecutableWriterHolder writeWatchTableImmediateWriterHolder((UINT64*)m_pWriteWatchTableImmediate, sizeof(UINT64));
+ *writeWatchTableImmediateWriterHolder.GetRW() = (size_t)g_sw_ww_table;
stompWBCompleteActions |= SWB_ICACHE_FLUSH;
}
break;
@@ -621,14 +627,16 @@ int WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSus
if (*(UINT64*)m_pCardTableImmediate != (size_t)g_card_table)
{
- *(UINT64*)m_pCardTableImmediate = (size_t)g_card_table;
+ ExecutableWriterHolder cardTableImmediateWriterHolder((UINT64*)m_pCardTableImmediate, sizeof(UINT64));
+ *cardTableImmediateWriterHolder.GetRW() = (size_t)g_card_table;
stompWBCompleteActions |= SWB_ICACHE_FLUSH;
}
#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
if (*(UINT64*)m_pCardBundleTableImmediate != (size_t)g_card_bundle_table)
{
- *(UINT64*)m_pCardBundleTableImmediate = (size_t)g_card_bundle_table;
+ ExecutableWriterHolder cardBundleTableImmediateWriterHolder((UINT64*)m_pCardBundleTableImmediate, sizeof(UINT64));
+ *cardBundleTableImmediateWriterHolder.GetRW() = (size_t)g_card_bundle_table;
stompWBCompleteActions |= SWB_ICACHE_FLUSH;
}
#endif
diff --git a/src/coreclr/vm/arm/armsinglestepper.cpp b/src/coreclr/vm/arm/armsinglestepper.cpp
index 79317263b2223..f9e718ae5420e 100644
--- a/src/coreclr/vm/arm/armsinglestepper.cpp
+++ b/src/coreclr/vm/arm/armsinglestepper.cpp
@@ -97,11 +97,7 @@ ArmSingleStepper::ArmSingleStepper()
ArmSingleStepper::~ArmSingleStepper()
{
#if !defined(DACCESS_COMPILE)
-#ifdef TARGET_UNIX
SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(WORD));
-#else
- DeleteExecutable(m_rgCode);
-#endif
#endif
}
@@ -110,11 +106,7 @@ void ArmSingleStepper::Init()
#if !defined(DACCESS_COMPILE)
if (m_rgCode == NULL)
{
-#ifdef TARGET_UNIX
m_rgCode = (WORD *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(WORD)));
-#else
- m_rgCode = new (executable) WORD[kMaxCodeBuffer];
-#endif
}
#endif
}
@@ -287,6 +279,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx)
DWORD idxNextInstruction = 0;
+ ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0]));
+
if (m_originalITState.InITBlock() && !ConditionHolds(pCtx, m_originalITState.CurrentCondition()))
{
LOG((LF_CORDB, LL_INFO100000, "ArmSingleStepper: Case 1: ITState::Clear;\n"));
@@ -295,7 +289,7 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx)
// to execute. We'll put the correct value back during fixup.
ITState::Clear(pCtx);
m_fSkipIT = true;
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
}
else if (TryEmulate(pCtx, opcode1, opcode2, false))
{
@@ -308,8 +302,8 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx)
m_fEmulate = true;
// Set breakpoints to stop the execution. This will get us right back here.
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
}
else
{
@@ -323,24 +317,24 @@ void ArmSingleStepper::Apply(T_CONTEXT *pCtx)
// guarantee one of them will be hit (we don't care which one -- the fixup code will update
// the PC and IT state to make it look as though the CPU just executed the current
// instruction).
- m_rgCode[idxNextInstruction++] = opcode1;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = opcode1;
if (Is32BitInstruction(opcode1))
- m_rgCode[idxNextInstruction++] = opcode2;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = opcode2;
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
}
// Always terminate the redirection buffer with a breakpoint.
- m_rgCode[idxNextInstruction++] = kBreakpointOp;
+ codeWriterHolder.GetRW()[idxNextInstruction++] = kBreakpointOp;
_ASSERTE(idxNextInstruction <= kMaxCodeBuffer);
// Set the thread up so it will redirect to our buffer when execution resumes.
pCtx->Pc = ((DWORD)(DWORD_PTR)m_rgCode) | THUMB_CODE;
// Make sure the CPU sees the updated contents of the buffer.
- FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode));
+ FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0]));
// Done, set the state.
m_state = Applied;
diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S
index 930395b56dc7e..3faa8fe36846e 100644
--- a/src/coreclr/vm/arm/asmhelpers.S
+++ b/src/coreclr/vm/arm/asmhelpers.S
@@ -978,6 +978,16 @@ g_rgWriteBarrierDescriptors:
.global g_rgWriteBarrierDescriptors
+// ------------------------------------------------------------------
+// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
+ LEAF_ENTRY JIT_WriteBarrier_Callable
+
+ // Branch to the write barrier
+ ldr r2, =JIT_WriteBarrier_Loc // or R3? See targetarm.h
+ ldr pc, [r2]
+
+ LEAF_END JIT_WriteBarrier_Callable
+
#ifdef FEATURE_READYTORUN
NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler
diff --git a/src/coreclr/vm/arm/asmhelpers.asm b/src/coreclr/vm/arm/asmhelpers.asm
index d20540e62090e..82596e66693dc 100644
--- a/src/coreclr/vm/arm/asmhelpers.asm
+++ b/src/coreclr/vm/arm/asmhelpers.asm
@@ -1724,6 +1724,18 @@ tempReg SETS "$tmpReg"
END_WRITE_BARRIERS
+ IMPORT JIT_WriteBarrier_Loc
+
+; ------------------------------------------------------------------
+; __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
+ LEAF_ENTRY JIT_WriteBarrier_Callable
+
+ ; Branch to the write barrier
+ ldr r2, =JIT_WriteBarrier_Loc ; or R3? See targetarm.h
+ ldr pc, [r2]
+
+ LEAF_END
+
#ifdef FEATURE_READYTORUN
NESTED_ENTRY DelayLoad_MethodCall_FakeProlog
diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h
index 88d0c6802b69d..425c286558432 100644
--- a/src/coreclr/vm/arm/cgencpu.h
+++ b/src/coreclr/vm/arm/cgencpu.h
@@ -1069,6 +1069,7 @@ struct StubPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
void ResetTargetInterlocked()
{
CONTRACTL
@@ -1095,6 +1096,7 @@ struct StubPrecode {
return (TADDR)InterlockedCompareExchange(
(LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected;
}
+#endif // !DACCESS_COMPILE
#ifdef FEATURE_PREJIT
void Fixup(DataImage *image);
@@ -1167,6 +1169,13 @@ struct FixupPrecode {
return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode);
}
+ size_t GetSizeRW()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return GetBase() + sizeof(void*) - dac_cast(this);
+ }
+
TADDR GetMethodDesc();
PCODE GetTarget()
@@ -1175,6 +1184,7 @@ struct FixupPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
void ResetTargetInterlocked()
{
CONTRACTL
@@ -1201,6 +1211,7 @@ struct FixupPrecode {
return (TADDR)InterlockedCompareExchange(
(LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == expected;
}
+#endif // !DACCESS_COMPILE
static BOOL IsFixupPrecodeByASM(PCODE addr)
{
@@ -1256,6 +1267,7 @@ struct ThisPtrRetBufPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
BOOL SetTargetInterlocked(TADDR target, TADDR expected)
{
CONTRACTL
@@ -1268,6 +1280,7 @@ struct ThisPtrRetBufPrecode {
ExecutableWriterHolder precodeWriterHolder(this, sizeof(ThisPtrRetBufPrecode));
return FastInterlockCompareExchange((LONG*)&precodeWriterHolder.GetRW()->m_pTarget, (LONG)target, (LONG)expected) == (LONG)expected;
}
+#endif // !DACCESS_COMPILE
};
typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode;
diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp
index aac3e25b18146..6e62df2370338 100644
--- a/src/coreclr/vm/arm/stubs.cpp
+++ b/src/coreclr/vm/arm/stubs.cpp
@@ -329,16 +329,28 @@ void ComputeWriteBarrierRange(BYTE ** ppbStart, DWORD * pcbLength)
{
DWORD size = (PBYTE)JIT_PatchedWriteBarrierLast - (PBYTE)JIT_PatchedWriteBarrierStart;
*ppbStart = (PBYTE)JIT_PatchedWriteBarrierStart;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ *ppbStart = GetWriteBarrierCodeLocation(*ppbStart);
+ }
*pcbLength = size;
}
void CopyWriteBarrier(PCODE dstCode, PCODE srcCode, PCODE endCode)
{
- TADDR dst = PCODEToPINSTR(dstCode);
+ TADDR dst = (TADDR)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation((void*)dstCode));
TADDR src = PCODEToPINSTR(srcCode);
TADDR end = PCODEToPINSTR(endCode);
size_t size = (PBYTE)end - (PBYTE)src;
+
+ ExecutableWriterHolder writeBarrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ writeBarrierWriterHolder = ExecutableWriterHolder((void*)dst, size);
+ dst = (TADDR)writeBarrierWriterHolder.GetRW();
+ }
+
memcpy((PVOID)dst, (PVOID)src, size);
}
@@ -419,7 +431,7 @@ void UpdateGCWriteBarriers(bool postGrow = false)
}
#define GWB_PATCH_OFFSET(_global) \
if (pDesc->m_dw_##_global##_offset != 0xffff) \
- PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset - 1), (UINT32)(dac_cast(_global)));
+ PutThumb2Mov32((UINT16*)(to + pDesc->m_dw_##_global##_offset), (UINT32)(dac_cast(_global)));
// Iterate through the write barrier patch table created in the .clrwb section
// (see write barrier asm code)
@@ -431,6 +443,13 @@ void UpdateGCWriteBarriers(bool postGrow = false)
PBYTE to = FindWBMapping(pDesc->m_pFuncStart);
if(to)
{
+ to = (PBYTE)PCODEToPINSTR((PCODE)GetWriteBarrierCodeLocation(to));
+ ExecutableWriterHolder barrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ barrierWriterHolder = ExecutableWriterHolder(to, pDesc->m_pFuncEnd - pDesc->m_pFuncStart);
+ to = barrierWriterHolder.GetRW();
+ }
GWB_PATCH_OFFSET(g_lowest_address);
GWB_PATCH_OFFSET(g_highest_address);
GWB_PATCH_OFFSET(g_ephemeral_low);
diff --git a/src/coreclr/vm/arm64/arm64singlestepper.cpp b/src/coreclr/vm/arm64/arm64singlestepper.cpp
index d45925311a33e..6c1764647c9f2 100644
--- a/src/coreclr/vm/arm64/arm64singlestepper.cpp
+++ b/src/coreclr/vm/arm64/arm64singlestepper.cpp
@@ -46,11 +46,7 @@ Arm64SingleStepper::Arm64SingleStepper()
Arm64SingleStepper::~Arm64SingleStepper()
{
#if !defined(DACCESS_COMPILE)
-#ifdef TARGET_UNIX
SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(uint32_t));
-#else
- DeleteExecutable(m_rgCode);
-#endif
#endif
}
@@ -59,11 +55,7 @@ void Arm64SingleStepper::Init()
#if !defined(DACCESS_COMPILE)
if (m_rgCode == NULL)
{
-#ifdef TARGET_UNIX
m_rgCode = (uint32_t *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(uint32_t)));
-#else
- m_rgCode = new (executable) uint32_t[kMaxCodeBuffer];
-#endif
}
#endif
}
@@ -207,7 +199,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx)
unsigned int idxNextInstruction = 0;
- ExecutableWriterHolder codeWriterHolder(m_rgCode, sizeof(m_rgCode));
+ ExecutableWriterHolder codeWriterHolder(m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0]));
if (TryEmulate(pCtx, opcode, false))
{
@@ -230,7 +222,7 @@ void Arm64SingleStepper::Apply(T_CONTEXT *pCtx)
pCtx->Pc = (uint64_t)m_rgCode;
// Make sure the CPU sees the updated contents of the buffer.
- FlushInstructionCache(GetCurrentProcess(), m_rgCode, sizeof(m_rgCode));
+ FlushInstructionCache(GetCurrentProcess(), m_rgCode, kMaxCodeBuffer * sizeof(m_rgCode[0]));
// Done, set the state.
m_state = Applied;
diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S
index e6b47d07b2b0c..8ef66586cd22c 100644
--- a/src/coreclr/vm/arm64/asmhelpers.S
+++ b/src/coreclr/vm/arm64/asmhelpers.S
@@ -270,13 +270,9 @@ LOCAL_LABEL(EphemeralCheckEnabled):
ldr x7, [x12]
// Update wbs state
-#ifdef FEATURE_WRITEBARRIER_COPY
PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Table_Loc, x12
ldr x12, [x12]
add x12, x12, x9
-#else // FEATURE_WRITEBARRIER_COPY
- adr x12, LOCAL_LABEL(wbs_begin)
-#endif // FEATURE_WRITEBARRIER_COPY
stp x0, x1, [x12], 16
stp x2, x3, [x12], 16
@@ -295,16 +291,10 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
mov x14, x0 // x14 = dst
mov x15, x1 // x15 = val
-#ifdef FEATURE_WRITEBARRIER_COPY
-LOCAL_LABEL(Branch_JIT_WriteBarrier_Copy):
// Branch to the write barrier
PREPARE_EXTERNAL_VAR JIT_WriteBarrier_Loc, x17
ldr x17, [x17]
br x17
-#else // FEATURE_WRITEBARRIER_COPY
- // Branch to the write barrier
- b C_FUNC(JIT_WriteBarrier)
-#endif // FEATURE_WRITEBARRIER_COPY
LEAF_END JIT_WriteBarrier_Callable, _TEXT
.balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm
index ffbeb9fd1acb3..17d3a676940bd 100644
--- a/src/coreclr/vm/arm64/asmhelpers.asm
+++ b/src/coreclr/vm/arm64/asmhelpers.asm
@@ -61,6 +61,10 @@
#ifdef FEATURE_COMINTEROP
IMPORT CLRToCOMWorker
#endif // FEATURE_COMINTEROP
+
+ IMPORT JIT_WriteBarrier_Table_Loc
+ IMPORT JIT_WriteBarrier_Loc
+
TEXTAREA
;; LPVOID __stdcall GetCurrentIP(void);
@@ -308,6 +312,7 @@ ThePreStubPatchLabel
; x12 will be used for pointers
mov x8, x0
+ mov x9, x1
adrp x12, g_card_table
ldr x0, [x12, g_card_table]
@@ -346,7 +351,9 @@ EphemeralCheckEnabled
ldr x7, [x12, g_highest_address]
; Update wbs state
- adr x12, wbs_begin
+ adrp x12, JIT_WriteBarrier_Table_Loc
+ ldr x12, [x12, JIT_WriteBarrier_Table_Loc]
+ add x12, x12, x9
stp x0, x1, [x12], 16
stp x2, x3, [x12], 16
stp x4, x5, [x12], 16
@@ -355,9 +362,11 @@ EphemeralCheckEnabled
EPILOG_RESTORE_REG_PAIR fp, lr, #16!
EPILOG_RETURN
+ WRITE_BARRIER_END JIT_UpdateWriteBarrierState
+
; Begin patchable literal pool
ALIGN 64 ; Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
-
+ WRITE_BARRIER_ENTRY JIT_WriteBarrier_Table
wbs_begin
wbs_card_table
DCQ 0
@@ -375,14 +384,7 @@ wbs_lowest_address
DCQ 0
wbs_highest_address
DCQ 0
-
- WRITE_BARRIER_END JIT_UpdateWriteBarrierState
-
-; ------------------------------------------------------------------
-; End of the writeable code region
- LEAF_ENTRY JIT_PatchedCodeLast
- ret lr
- LEAF_END
+ WRITE_BARRIER_END JIT_WriteBarrier_Table
; void JIT_ByRefWriteBarrier
; On entry:
@@ -546,6 +548,12 @@ Exit
ret lr
WRITE_BARRIER_END JIT_WriteBarrier
+; ------------------------------------------------------------------
+; End of the writeable code region
+ LEAF_ENTRY JIT_PatchedCodeLast
+ ret lr
+ LEAF_END
+
#ifdef FEATURE_PREJIT
;------------------------------------------------
; VirtualMethodFixupStub
@@ -1417,9 +1425,10 @@ CallHelper2
mov x14, x0 ; x14 = dst
mov x15, x1 ; x15 = val
- ; Branch to the write barrier (which is already correctly overwritten with
- ; single or multi-proc code based on the current CPU
- b JIT_WriteBarrier
+ ; Branch to the write barrier
+ adrp x17, JIT_WriteBarrier_Loc
+ ldr x17, [x17, JIT_WriteBarrier_Loc]
+ br x17
LEAF_END
diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h
index 83e56cfb9f9b9..0641d89ff1a91 100644
--- a/src/coreclr/vm/arm64/cgencpu.h
+++ b/src/coreclr/vm/arm64/cgencpu.h
@@ -597,6 +597,7 @@ struct StubPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
void ResetTargetInterlocked()
{
CONTRACTL
@@ -623,6 +624,7 @@ struct StubPrecode {
return (TADDR)InterlockedCompareExchange64(
(LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected;
}
+#endif // !DACCESS_COMPILE
#ifdef FEATURE_PREJIT
void Fixup(DataImage *image);
@@ -715,6 +717,13 @@ struct FixupPrecode {
return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode);
}
+ size_t GetSizeRW()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return GetBase() + sizeof(void*) - dac_cast(this);
+ }
+
TADDR GetMethodDesc();
PCODE GetTarget()
@@ -723,6 +732,7 @@ struct FixupPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
void ResetTargetInterlocked()
{
CONTRACTL
@@ -749,6 +759,7 @@ struct FixupPrecode {
return (TADDR)InterlockedCompareExchange64(
(LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected;
}
+#endif // !DACCESS_COMPILE
static BOOL IsFixupPrecodeByASM(PCODE addr)
{
@@ -797,6 +808,7 @@ struct ThisPtrRetBufPrecode {
return m_pTarget;
}
+#ifndef DACCESS_COMPILE
BOOL SetTargetInterlocked(TADDR target, TADDR expected)
{
CONTRACTL
@@ -810,6 +822,7 @@ struct ThisPtrRetBufPrecode {
return (TADDR)InterlockedCompareExchange64(
(LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected;
}
+#endif // !DACCESS_COMPILE
};
typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode;
diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp
index 54cf1c4927548..12d56ddb9867e 100644
--- a/src/coreclr/vm/arm64/stubs.cpp
+++ b/src/coreclr/vm/arm64/stubs.cpp
@@ -1067,8 +1067,14 @@ extern "C" void STDCALL JIT_PatchedCodeLast();
static void UpdateWriteBarrierState(bool skipEphemeralCheck)
{
BYTE *writeBarrierCodeStart = GetWriteBarrierCodeLocation((void*)JIT_PatchedCodeStart);
- ExecutableWriterHolder writeBarrierWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart);
- JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierWriterHolder.GetRW() - writeBarrierCodeStart);
+ BYTE *writeBarrierCodeStartRW = writeBarrierCodeStart;
+ ExecutableWriterHolder writeBarrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ writeBarrierWriterHolder = ExecutableWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart);
+ writeBarrierCodeStartRW = writeBarrierWriterHolder.GetRW();
+ }
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierCodeStartRW - writeBarrierCodeStart);
}
void InitJITHelpers1()
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index cdc5925234af9..b60aac924d2e2 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -607,6 +607,11 @@ void EESocketCleanupHelper(bool isExecutingOnAltStack)
#endif // TARGET_UNIX
#endif // CROSSGEN_COMPILE
+void FatalErrorHandler(UINT errorCode, LPCWSTR pszMessage)
+{
+ EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(errorCode, pszMessage);
+}
+
void EEStartupHelper()
{
CONTRACTL
@@ -670,6 +675,8 @@ void EEStartupHelper()
// This needs to be done before the EE has started
InitializeStartupFlags();
+ IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler));
+
ThreadpoolMgr::StaticInitialize();
MethodDescBackpatchInfoTracker::StaticInitialize();
@@ -824,7 +831,7 @@ void EEStartupHelper()
g_runtimeLoadedBaseAddress = (SIZE_T)pe.GetBase();
g_runtimeVirtualSize = (SIZE_T)pe.GetVirtualSize();
- InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64));
+ ExecutableAllocator::InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64));
}
#endif // !TARGET_UNIX
diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp
index 02feec829a76b..5c5004f56860a 100644
--- a/src/coreclr/vm/class.cpp
+++ b/src/coreclr/vm/class.cpp
@@ -153,7 +153,9 @@ void EEClass::Destruct(MethodTable * pOwningMT)
if (pDelegateEEClass->m_pStaticCallStub)
{
- BOOL fStubDeleted = pDelegateEEClass->m_pStaticCallStub->DecRef();
+ ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pStaticCallStub, sizeof(Stub));
+ BOOL fStubDeleted = stubWriterHolder.GetRW()->DecRef();
+
if (fStubDeleted)
{
DelegateInvokeStubManager::g_pManager->RemoveStub(pDelegateEEClass->m_pStaticCallStub);
@@ -167,7 +169,6 @@ void EEClass::Destruct(MethodTable * pOwningMT)
// it is owned by the m_pMulticastStubCache, not by the class
// - it is shared across classes. So we don't decrement
// its ref count here
- delete pDelegateEEClass->m_pUMThunkMarshInfo;
}
#ifdef FEATURE_COMINTEROP
diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp
index 37220786fedda..78721292a3e9f 100644
--- a/src/coreclr/vm/codeman.cpp
+++ b/src/coreclr/vm/codeman.cpp
@@ -2139,8 +2139,7 @@ VOID EEJitManager::EnsureJumpStubReserve(BYTE * pImageBase, SIZE_T imageSize, SI
return; // Unable to allocate the reserve - give up
}
- pNewReserve->m_ptr = ClrVirtualAllocWithinRange(loAddrCurrent, hiAddrCurrent,
- allocChunk, MEM_RESERVE, PAGE_NOACCESS);
+ pNewReserve->m_ptr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(allocChunk, loAddrCurrent, hiAddrCurrent);
if (pNewReserve->m_ptr != NULL)
break;
@@ -2231,8 +2230,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap
if (!pInfo->getThrowOnOutOfMemoryWithinRange() && PEDecoder::GetForceRelocs())
RETURN NULL;
#endif
- pBaseAddr = ClrVirtualAllocWithinRange(loAddr, hiAddr,
- reserveSize, MEM_RESERVE, PAGE_NOACCESS);
+ pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(reserveSize, loAddr, hiAddr);
if (!pBaseAddr)
{
@@ -2251,7 +2249,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap
}
else
{
- pBaseAddr = ClrVirtualAllocExecutable(reserveSize, MEM_RESERVE, PAGE_NOACCESS);
+ pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(reserveSize);
if (!pBaseAddr)
ThrowOutOfMemory();
}
@@ -2686,15 +2684,14 @@ void EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, size_t reserveFo
*pAllocatedSize = sizeof(CodeHeader) + totalSize;
-#if defined(HOST_OSX) && defined(HOST_ARM64)
-#define FEATURE_WXORX
-#endif
-
-#ifdef FEATURE_WXORX
- pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize];
-#else
- pCodeHdrRW = pCodeHdr;
-#endif
+ if (ExecutableAllocator::IsWXORXEnabled())
+ {
+ pCodeHdrRW = (CodeHeader *)new BYTE[*pAllocatedSize];
+ }
+ else
+ {
+ pCodeHdrRW = pCodeHdr;
+ }
#ifdef USE_INDIRECT_CODEHEADER
if (requestInfo.IsDynamicDomain())
@@ -3347,7 +3344,7 @@ void EEJitManager::Unload(LoaderAllocator *pAllocator)
}
}
- ResetCodeAllocHint();
+ ExecutableAllocator::ResetCodeAllocHint();
}
EEJitManager::DomainCodeHeapList::DomainCodeHeapList()
diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp
index 8b95dac8cdd77..499880dc16dde 100644
--- a/src/coreclr/vm/comcallablewrapper.cpp
+++ b/src/coreclr/vm/comcallablewrapper.cpp
@@ -3183,12 +3183,11 @@ void ComMethodTable::Cleanup()
if (m_pDispatchInfo)
delete m_pDispatchInfo;
- if (m_pMDescr)
- DeleteExecutable(m_pMDescr);
if (m_pITypeInfo && !g_fProcessDetach)
SafeRelease(m_pITypeInfo);
- DeleteExecutable(this);
+ // The m_pMDescr and the current instance is allocated from the related LoaderAllocator
+ // so no cleanup is needed here.
}
@@ -3214,7 +3213,7 @@ void ComMethodTable::LayOutClassMethodTable()
SLOT *pComVtable;
unsigned cbPrevSlots = 0;
unsigned cbAlloc = 0;
- NewExecutableHolder pMDMemoryPtr = NULL;
+ AllocMemHolder pMDMemoryPtr;
BYTE* pMethodDescMemory = NULL;
size_t writeableOffset = 0;
unsigned cbNumParentVirtualMethods = 0;
@@ -3321,7 +3320,7 @@ void ComMethodTable::LayOutClassMethodTable()
cbAlloc = cbMethodDescs;
if (cbAlloc > 0)
{
- pMDMemoryPtr = (BYTE*) new (executable) BYTE[cbAlloc + sizeof(UINT_PTR)];
+ pMDMemoryPtr = m_pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbAlloc + sizeof(UINT_PTR)));
pMethodDescMemory = pMDMemoryPtr;
methodDescMemoryWriteableHolder = ExecutableWriterHolder(pMethodDescMemory, cbAlloc + sizeof(UINT_PTR));
@@ -3703,7 +3702,6 @@ BOOL ComMethodTable::LayOutInterfaceMethodTable(MethodTable* pClsMT)
// Method descs are at the end of the vtable
// m_cbSlots interfaces methods + IUnk methods
pMethodDescMemory = (BYTE *)&pComVtable[m_cbSlots];
-
for (i = 0; i < cbSlots; i++)
{
ComCallMethodDesc* pNewMD = (ComCallMethodDesc *) (pMethodDescMemory + COMMETHOD_PREPAD);
@@ -4495,13 +4493,12 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForClass(MethodTable
if (cbToAlloc.IsOverflow())
ThrowHR(COR_E_OVERFLOW);
- NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()];
+ AllocMemHolder pComMT(pClassMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value())));
_ASSERTE(!cbNewSlots.IsOverflow() && !cbTotalSlots.IsOverflow() && !cbVtable.IsOverflow());
ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc.Value());
ComMethodTable* pComMTRW = comMTWriterHolder.GetRW();
-
// set up the header
pComMTRW->m_ptReserved = (SLOT)(size_t)0xDEADC0FF; // reserved
pComMTRW->m_pMT = pClassMT; // pointer to the class method table
@@ -4573,7 +4570,7 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForInterface(MethodT
if (cbToAlloc.IsOverflow())
ThrowHR(COR_E_OVERFLOW);
- NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc.Value()];
+ AllocMemHolder pComMT(pInterfaceMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc.Value())));
_ASSERTE(!cbVtable.IsOverflow() && !cbMethDescs.IsOverflow());
@@ -4639,7 +4636,8 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForBasic(MethodTable
unsigned cbVtable = cbExtraSlots * sizeof(SLOT);
unsigned cbToAlloc = sizeof(ComMethodTable) + cbVtable;
- NewExecutableHolder pComMT = (ComMethodTable*) new (executable) BYTE[cbToAlloc];
+ AllocMemHolder pComMT(pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(cbToAlloc)));
+
ExecutableWriterHolder comMTWriterHolder(pComMT, cbToAlloc);
ComMethodTable* pComMTRW = comMTWriterHolder.GetRW();
diff --git a/src/coreclr/vm/comcallablewrapper.h b/src/coreclr/vm/comcallablewrapper.h
index 2581ddf832fd5..0f1e4b878e4c9 100644
--- a/src/coreclr/vm/comcallablewrapper.h
+++ b/src/coreclr/vm/comcallablewrapper.h
@@ -499,6 +499,7 @@ struct ComMethodTable
// Accessor for the IDispatch information.
DispatchInfo* GetDispatchInfo();
+#ifndef DACCESS_COMPILE
LONG AddRef()
{
LIMITED_METHOD_CONTRACT;
@@ -527,6 +528,7 @@ struct ComMethodTable
return cbRef;
}
+#endif // DACCESS_COMPILE
CorIfaceAttr GetInterfaceType()
{
@@ -746,6 +748,7 @@ struct ComMethodTable
}
+#ifndef DACCESS_COMPILE
inline REFIID GetIID()
{
// Cannot use a normal CONTRACT since the return type is ref type which
@@ -768,6 +771,7 @@ struct ComMethodTable
return m_IID;
}
+#endif // DACCESS_COMPILE
void CheckParentComVisibility(BOOL fForIDispatch)
{
diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp
index b6c17260a1302..03cecd838023b 100644
--- a/src/coreclr/vm/comdelegate.cpp
+++ b/src/coreclr/vm/comdelegate.cpp
@@ -1253,7 +1253,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj)
{
GCX_PREEMP();
- pUMThunkMarshInfo = new UMThunkMarshInfo();
+ pUMThunkMarshInfo = (UMThunkMarshInfo*)(void*)pMT->GetLoaderAllocator()->GetStubHeap()->AllocMem(S_SIZE_T(sizeof(UMThunkMarshInfo)));
ExecutableWriterHolder uMThunkMarshInfoWriterHolder(pUMThunkMarshInfo, sizeof(UMThunkMarshInfo));
uMThunkMarshInfoWriterHolder.GetRW()->LoadTimeInit(pInvokeMeth);
@@ -1263,7 +1263,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj)
pUMThunkMarshInfo,
NULL ) != NULL)
{
- delete pUMThunkMarshInfo;
+ pMT->GetLoaderAllocator()->GetStubHeap()->BackoutMem(pUMThunkMarshInfo, sizeof(UMThunkMarshInfo));
pUMThunkMarshInfo = pClass->m_pUMThunkMarshInfo;
}
}
diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp
index 39f00d0674193..15a33c711e7a9 100644
--- a/src/coreclr/vm/comsynchronizable.cpp
+++ b/src/coreclr/vm/comsynchronizable.cpp
@@ -1089,22 +1089,13 @@ FCIMPL1(void, ThreadNative::SetIsThreadpoolThread, ThreadBaseObject* thread)
}
FCIMPLEND
-INT32 QCALLTYPE ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration()
+FCIMPL0(INT32, ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
{
- QCALL_CONTRACT;
-
- INT32 optimalMaxNormalizedYieldsPerSpinIteration;
-
- BEGIN_QCALL;
-
- // RuntimeThread calls this function only once lazily and caches the result, so ensure initialization
- EnsureYieldProcessorNormalizedInitialized();
- optimalMaxNormalizedYieldsPerSpinIteration = g_optimalMaxNormalizedYieldsPerSpinIteration;
-
- END_QCALL;
+ FCALL_CONTRACT;
- return optimalMaxNormalizedYieldsPerSpinIteration;
+ return (INT32)YieldProcessorNormalization::GetOptimalMaxNormalizedYieldsPerSpinIteration();
}
+FCIMPLEND
FCIMPL1(void, ThreadNative::SpinWait, int iterations)
{
diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h
index e9968201b8bc2..cfab18d901070 100644
--- a/src/coreclr/vm/comsynchronizable.h
+++ b/src/coreclr/vm/comsynchronizable.h
@@ -86,7 +86,7 @@ friend class ThreadBaseObject;
UINT64 QCALLTYPE GetProcessDefaultStackSize();
static FCDECL1(INT32, GetManagedThreadId, ThreadBaseObject* th);
- static INT32 QCALLTYPE GetOptimalMaxSpinWaitsPerSpinIteration();
+ static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration);
static FCDECL1(void, SpinWait, int iterations);
static BOOL QCALLTYPE YieldThread();
static FCDECL0(Object*, GetCurrentThread);
diff --git a/src/coreclr/vm/custommarshalerinfo.cpp b/src/coreclr/vm/custommarshalerinfo.cpp
index 67acbff136ec3..0af48f8e71576 100644
--- a/src/coreclr/vm/custommarshalerinfo.cpp
+++ b/src/coreclr/vm/custommarshalerinfo.cpp
@@ -67,13 +67,6 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type
STRINGREF CookieStringObj = StringObject::NewString(strCookie, cCookieStrBytes);
GCPROTECT_BEGIN(CookieStringObj);
#endif
-
- // Load the method desc's for all the methods in the ICustomMarshaler interface.
- m_pMarshalNativeToManagedMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalNativeToManaged, hndCustomMarshalerType);
- m_pMarshalManagedToNativeMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalManagedToNative, hndCustomMarshalerType);
- m_pCleanUpNativeDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpNativeData, hndCustomMarshalerType);
- m_pCleanUpManagedDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpManagedData, hndCustomMarshalerType);
-
// Load the method desc for the static method to retrieve the instance.
MethodDesc *pGetCustomMarshalerMD = GetCustomMarshalerMD(CustomMarshalerMethods_GetInstance, hndCustomMarshalerType);
@@ -103,7 +96,9 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type
};
// Call the GetCustomMarshaler method to retrieve the custom marshaler to use.
- OBJECTREF CustomMarshalerObj = getCustomMarshaler.Call_RetOBJECTREF(GetCustomMarshalerArgs);
+ OBJECTREF CustomMarshalerObj = NULL;
+ GCPROTECT_BEGIN(CustomMarshalerObj);
+ CustomMarshalerObj = getCustomMarshaler.Call_RetOBJECTREF(GetCustomMarshalerArgs);
if (!CustomMarshalerObj)
{
DefineFullyQualifiedNameForClassW()
@@ -111,7 +106,16 @@ CustomMarshalerInfo::CustomMarshalerInfo(LoaderAllocator *pLoaderAllocator, Type
IDS_EE_NOCUSTOMMARSHALER,
GetFullyQualifiedNameForClassW(hndCustomMarshalerType.GetMethodTable()));
}
+ // Load the method desc's for all the methods in the ICustomMarshaler interface based on the type of the marshaler object.
+ TypeHandle customMarshalerObjType = CustomMarshalerObj->GetMethodTable();
+
+ m_pMarshalNativeToManagedMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalNativeToManaged, customMarshalerObjType);
+ m_pMarshalManagedToNativeMD = GetCustomMarshalerMD(CustomMarshalerMethods_MarshalManagedToNative, customMarshalerObjType);
+ m_pCleanUpNativeDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpNativeData, customMarshalerObjType);
+ m_pCleanUpManagedDataMD = GetCustomMarshalerMD(CustomMarshalerMethods_CleanUpManagedData, customMarshalerObjType);
+
m_hndCustomMarshaler = pLoaderAllocator->AllocateHandle(CustomMarshalerObj);
+ GCPROTECT_END();
// Retrieve the size of the native data.
if (m_bDataIsByValue)
diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp
index 4a88f81df5210..4f3cf879d10a4 100644
--- a/src/coreclr/vm/dllimportcallback.cpp
+++ b/src/coreclr/vm/dllimportcallback.cpp
@@ -41,7 +41,7 @@ class UMEntryThunkFreeList
{
WRAPPER_NO_CONTRACT;
- m_crst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE);
+ m_crst.Init(CrstUMEntryThunkFreeListLock, CRST_UNSAFE_ANYMODE);
}
UMEntryThunk *GetUMEntryThunk()
diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp
index 9dae86aca9377..541d88dc16885 100644
--- a/src/coreclr/vm/dynamicmethod.cpp
+++ b/src/coreclr/vm/dynamicmethod.cpp
@@ -403,8 +403,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo)
if (pInfo->m_loAddr != NULL || pInfo->m_hiAddr != NULL)
{
- m_pBaseAddr = ClrVirtualAllocWithinRange(pInfo->m_loAddr, pInfo->m_hiAddr,
- ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS);
+ m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->ReserveWithinRange(ReserveBlockSize, pInfo->m_loAddr, pInfo->m_hiAddr);
if (!m_pBaseAddr)
{
if (pInfo->getThrowOnOutOfMemoryWithinRange())
@@ -417,7 +416,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo)
// top up the ReserveBlockSize to suggested minimum
ReserveBlockSize = max(ReserveBlockSize, pInfo->getReserveSize());
- m_pBaseAddr = ClrVirtualAllocExecutable(ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS);
+ m_pBaseAddr = (BYTE*)ExecutableAllocator::Instance()->Reserve(ReserveBlockSize);
if (!m_pBaseAddr)
ThrowOutOfMemory();
}
@@ -749,7 +748,7 @@ HostCodeHeap::TrackAllocation* HostCodeHeap::AllocMemory_NoThrow(size_t header,
if (m_pLastAvailableCommittedAddr + sizeToCommit <= m_pBaseAddr + m_TotalBytesAvailable)
{
- if (NULL == ClrVirtualAlloc(m_pLastAvailableCommittedAddr, sizeToCommit, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
+ if (NULL == ExecutableAllocator::Instance()->Commit(m_pLastAvailableCommittedAddr, sizeToCommit, true /* isExecutable */))
{
LOG((LF_BCL, LL_ERROR, "CodeHeap [0x%p] - VirtualAlloc failed\n", this));
return NULL;
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index f77dc75c80b5c..ea3f65d72917d 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -602,7 +602,7 @@ FCFuncStart(gThreadFuncs)
#endif // FEATURE_COMINTEROP
FCFuncElement("Interrupt", ThreadNative::Interrupt)
FCFuncElement("Join", ThreadNative::Join)
- QCFuncElement("GetOptimalMaxSpinWaitsPerSpinIterationInternal", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
+ FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
FCFuncElement("GetCurrentProcessorNumber", ThreadNative::GetCurrentProcessorNumber)
FCFuncEnd()
diff --git a/src/coreclr/vm/eeprofinterfaces.inl b/src/coreclr/vm/eeprofinterfaces.inl
index 250b3700f801a..da6e978832968 100644
--- a/src/coreclr/vm/eeprofinterfaces.inl
+++ b/src/coreclr/vm/eeprofinterfaces.inl
@@ -31,5 +31,13 @@ FORCEINLINE BOOL TrackLargeAllocations()
#endif // PROFILING_SUPPORTED
}
+FORCEINLINE BOOL TrackPinnedAllocations()
+{
+#ifdef PROFILING_SUPPORTED
+ return CORProfilerTrackPinnedAllocations();
+#else
+ return FALSE;
+#endif // PROFILING_SUPPORTED
+}
#endif
diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
index 607286d048bab..7ee83ae8c5f84 100644
--- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
+++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
@@ -1169,7 +1169,22 @@ ep_rt_entrypoint_assembly_name_get_utf8 (void)
{
STATIC_CONTRACT_NOTHROW;
- return reinterpret_cast(GetAppDomain ()->GetRootAssembly ()->GetSimpleName ());
+ AppDomain *app_domain_ref = nullptr;
+ Assembly *assembly_ref = nullptr;
+
+ app_domain_ref = GetAppDomain ();
+ if (app_domain_ref != nullptr)
+ {
+ assembly_ref = app_domain_ref->GetRootAssembly ();
+ if (assembly_ref != nullptr)
+ {
+ return reinterpret_cast(assembly_ref->GetSimpleName ());
+ }
+ }
+
+ // fallback to the empty string if we can't get assembly info, e.g., if the runtime is
+ // suspended before an assembly is loaded.
+ return reinterpret_cast("");
}
static
diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp
index ac7be2a94398f..aded74deda619 100644
--- a/src/coreclr/vm/eventtrace.cpp
+++ b/src/coreclr/vm/eventtrace.cpp
@@ -4417,6 +4417,12 @@ VOID EtwCallbackCommon(
{
ETW::TypeSystemLog::OnKeywordsChanged();
}
+
+ if (g_fEEStarted && !g_fEEShutDown)
+ {
+ // Emit the YieldProcessor measured values at the beginning of the trace
+ YieldProcessorNormalization::FireMeasurementEvents();
+ }
}
// Individual callbacks for each EventPipe provider.
diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp
index a1fdf255a5ce0..6bf5efcc8028c 100644
--- a/src/coreclr/vm/excep.cpp
+++ b/src/coreclr/vm/excep.cpp
@@ -6699,14 +6699,12 @@ AdjustContextForJITHelpers(
PCODE ip = GetIP(pContext);
-#ifdef FEATURE_WRITEBARRIER_COPY
if (IsIPInWriteBarrierCodeCopy(ip))
{
// Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame
ip = AdjustWriteBarrierIP(ip);
SetIP(pContext, ip);
}
-#endif // FEATURE_WRITEBARRIER_COPY
#ifdef FEATURE_DATABREAKPOINT
diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp
index 7fff234ca85ef..4af702fab1499 100644
--- a/src/coreclr/vm/exceptionhandling.cpp
+++ b/src/coreclr/vm/exceptionhandling.cpp
@@ -4694,14 +4694,12 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
break;
}
-#ifdef FEATURE_WRITEBARRIER_COPY
if (IsIPInWriteBarrierCodeCopy(controlPc))
{
// Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame
controlPc = AdjustWriteBarrierIP(controlPc);
SetIP(frameContext, controlPc);
}
-#endif // FEATURE_WRITEBARRIER_COPY
UINT_PTR sp = GetSP(frameContext);
@@ -5174,13 +5172,11 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e
{
PCODE controlPc = GetIP(contextRecord);
-#ifdef FEATURE_WRITEBARRIER_COPY
if (IsIPInWriteBarrierCodeCopy(controlPc))
{
// Pretend we were executing the barrier function at its original location
controlPc = AdjustWriteBarrierIP(controlPc);
}
-#endif // FEATURE_WRITEBARRIER_COPY
return g_fEEStarted && (
exceptionRecord->ExceptionCode == STATUS_BREAKPOINT ||
@@ -5259,14 +5255,12 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
{
GCX_COOP(); // Must be cooperative to modify frame chain.
-#ifdef FEATURE_WRITEBARRIER_COPY
if (IsIPInWriteBarrierCodeCopy(controlPc))
{
// Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame
controlPc = AdjustWriteBarrierIP(controlPc);
SetIP(ex->GetContextRecord(), controlPc);
}
-#endif // FEATURE_WRITEBARRIER_COPY
if (IsIPInMarkedJitHelper(controlPc))
{
diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp
index 1e4dbf913c898..e8370315e6665 100644
--- a/src/coreclr/vm/finalizerthread.cpp
+++ b/src/coreclr/vm/finalizerthread.cpp
@@ -379,11 +379,6 @@ DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args)
{
GetFinalizerThread()->SetBackground(TRUE);
- {
- GCX_PREEMP();
- EnsureYieldProcessorNormalizedInitialized();
- }
-
while (!fQuitFinalizer)
{
// This will apply any policy for swallowing exceptions during normal
diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp
index be856dbe1a63a..9ce0cc676f7a7 100644
--- a/src/coreclr/vm/gccover.cpp
+++ b/src/coreclr/vm/gccover.cpp
@@ -1258,9 +1258,9 @@ void RemoveGcCoverageInterrupt(TADDR instrPtr, BYTE * savedInstrPtr, GCCoverageI
{
ExecutableWriterHolder instrPtrWriterHolder((void*)instrPtr, 4);
#ifdef TARGET_ARM
- if (GetARMInstructionLength(savedInstrPtr) == 2)
+ if (GetARMInstructionLength(savedInstrPtr) == 2)
*(WORD *)instrPtrWriterHolder.GetRW() = *(WORD *)savedInstrPtr;
- else
+ else
*(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr;
#elif defined(TARGET_ARM64)
*(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr;
diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp
index 0cecfc624a744..01ffd5305d9f0 100644
--- a/src/coreclr/vm/gchelpers.cpp
+++ b/src/coreclr/vm/gchelpers.cpp
@@ -324,7 +324,8 @@ void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags)
// Notify the profiler of the allocation
// do this after initializing bounds so callback has size information
if (TrackAllocations() ||
- (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP))
+ (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP) ||
+ (TrackPinnedAllocations() && flags & GC_ALLOC_PINNED_OBJECT_HEAP))
{
OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
GCPROTECT_BEGIN(objref);
diff --git a/src/coreclr/vm/i386/jithelp.S b/src/coreclr/vm/i386/jithelp.S
index facce7cacd3ef..dc56da1d1779e 100644
--- a/src/coreclr/vm/i386/jithelp.S
+++ b/src/coreclr/vm/i386/jithelp.S
@@ -377,10 +377,27 @@ LEAF_ENTRY JIT_WriteBarrierGroup, _TEXT
ret
LEAF_END JIT_WriteBarrierGroup, _TEXT
-#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
-// *******************************************************************************
-// Write barrier wrappers with fcall calling convention
-//
+ .data
+ .align 4
+ .global C_FUNC(JIT_WriteBarrierEAX_Loc)
+C_FUNC(JIT_WriteBarrierEAX_Loc):
+ .word 0
+ .text
+
+LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT
+ mov eax, edx
+ mov edx, ecx
+ push eax
+ call 1f
+1:
+ pop eax
+2:
+ add eax, offset _GLOBAL_OFFSET_TABLE_+1 // (2b - 1b)
+ mov eax, dword ptr [eax + C_FUNC(JIT_WriteBarrierEAX_Loc)@GOT]
+ xchg eax, dword ptr [esp]
+ ret
+LEAF_END JIT_WriteBarrier_Callable, _TEXT
+
.macro UniversalWriteBarrierHelper name
.align 4
@@ -392,6 +409,11 @@ LEAF_END JIT_\name, _TEXT
.endm
+#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
+// *******************************************************************************
+// Write barrier wrappers with fcall calling convention
+//
+
// Only define these if we're using the ASM GC write barriers; if this flag is not defined,
// we'll use C++ versions of these write barriers.
UniversalWriteBarrierHelper CheckedWriteBarrier
diff --git a/src/coreclr/vm/i386/jithelp.asm b/src/coreclr/vm/i386/jithelp.asm
index 3743ac3cbe02f..3650b3f2afd6d 100644
--- a/src/coreclr/vm/i386/jithelp.asm
+++ b/src/coreclr/vm/i386/jithelp.asm
@@ -411,15 +411,13 @@ ENDM
;*******************************************************************************
; Write barrier wrappers with fcall calling convention
;
-UniversalWriteBarrierHelper MACRO name
+
+ .data
ALIGN 4
-PUBLIC @JIT_&name&@8
-@JIT_&name&@8 PROC
- mov eax,edx
- mov edx,ecx
- jmp _JIT_&name&EAX@0
-@JIT_&name&@8 ENDP
-ENDM
+ public _JIT_WriteBarrierEAX_Loc
+_JIT_WriteBarrierEAX_Loc dd 0
+
+ .code
; WriteBarrierStart and WriteBarrierEnd are used to determine bounds of
; WriteBarrier functions so can determine if got AV in them.
@@ -429,6 +427,25 @@ _JIT_WriteBarrierGroup@0 PROC
ret
_JIT_WriteBarrierGroup@0 ENDP
+ ALIGN 4
+PUBLIC @JIT_WriteBarrier_Callable@8
+@JIT_WriteBarrier_Callable@8 PROC
+ mov eax,edx
+ mov edx,ecx
+ jmp DWORD PTR [_JIT_WriteBarrierEAX_Loc]
+
+@JIT_WriteBarrier_Callable@8 ENDP
+
+UniversalWriteBarrierHelper MACRO name
+ ALIGN 4
+PUBLIC @JIT_&name&@8
+@JIT_&name&@8 PROC
+ mov eax,edx
+ mov edx,ecx
+ jmp _JIT_&name&EAX@0
+@JIT_&name&@8 ENDP
+ENDM
+
ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
; Only define these if we're using the ASM GC write barriers; if this flag is not defined,
; we'll use C++ versions of these write barriers.
@@ -1233,6 +1250,8 @@ fremloopd:
; PatchedCodeStart and PatchedCodeEnd are used to determine bounds of patched code.
;
+ ALIGN 4
+
_JIT_PatchedCodeStart@0 proc public
ret
_JIT_PatchedCodeStart@0 endp
diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp
index 0e366bdbd1a8b..0467f347aaacb 100644
--- a/src/coreclr/vm/i386/jitinterfacex86.cpp
+++ b/src/coreclr/vm/i386/jitinterfacex86.cpp
@@ -1050,10 +1050,18 @@ void InitJITHelpers1()
{
BYTE * pfunc = (BYTE *) JIT_WriteBarrierReg_PreGrow;
- BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier];
+ BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]);
int reg = c_rgWriteBarrierRegs[iBarrier];
- memcpy(pBuf, pfunc, 34);
+ BYTE * pBufRW = pBuf;
+ ExecutableWriterHolder barrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ barrierWriterHolder = ExecutableWriterHolder(pBuf, 34);
+ pBufRW = barrierWriterHolder.GetRW();
+ }
+
+ memcpy(pBufRW, pfunc, 34);
// assert the copied code ends in a ret to make sure we got the right length
_ASSERTE(pBuf[33] == 0xC3);
@@ -1069,24 +1077,24 @@ void InitJITHelpers1()
_ASSERTE(pBuf[0] == 0x89);
// Update the reg field (bits 3..5) of the ModR/M byte of this instruction
- pBuf[1] &= 0xc7;
- pBuf[1] |= reg << 3;
+ pBufRW[1] &= 0xc7;
+ pBufRW[1] |= reg << 3;
// Second instruction to patch is cmp reg, imm32 (low bound)
_ASSERTE(pBuf[2] == 0x81);
// Here the lowest three bits in ModR/M field are the register
- pBuf[3] &= 0xf8;
- pBuf[3] |= reg;
+ pBufRW[3] &= 0xf8;
+ pBufRW[3] |= reg;
#ifdef WRITE_BARRIER_CHECK
// Don't do the fancy optimization just jump to the old one
// Use the slow one from time to time in a debug build because
// there are some good asserts in the unoptimized one
if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK) || DEBUG_RANDOM_BARRIER_CHECK) {
- pfunc = &pBuf[0];
+ pfunc = &pBufRW[0];
*pfunc++ = 0xE9; // JMP c_rgDebugWriteBarriers[iBarrier]
- *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (pfunc + sizeof(DWORD));
+ *((DWORD*) pfunc) = (BYTE*) c_rgDebugWriteBarriers[iBarrier] - (&pBuf[1] + sizeof(DWORD));
}
#endif // WRITE_BARRIER_CHECK
}
@@ -1132,7 +1140,7 @@ void ValidateWriteBarrierHelpers()
#endif // WRITE_BARRIER_CHECK
// first validate the PreGrow helper
- BYTE* pWriteBarrierFunc = reinterpret_cast(JIT_WriteBarrierEAX);
+ BYTE* pWriteBarrierFunc = GetWriteBarrierCodeLocation(reinterpret_cast(JIT_WriteBarrierEAX));
// ephemeral region
DWORD* pLocation = reinterpret_cast(&pWriteBarrierFunc[AnyGrow_EphemeralLowerBound]);
@@ -1170,7 +1178,7 @@ void ValidateWriteBarrierHelpers()
#endif //CODECOVERAGE
/*********************************************************************/
-#define WriteBarrierIsPreGrow() (((BYTE *)JIT_WriteBarrierEAX)[10] == 0xc1)
+#define WriteBarrierIsPreGrow() ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[10] == 0xc1)
/*********************************************************************/
@@ -1188,20 +1196,28 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */)
#ifdef WRITE_BARRIER_CHECK
// Don't do the fancy optimization if we are checking write barrier
- if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier
+ if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier
return stompWBCompleteActions;
#endif // WRITE_BARRIER_CHECK
// Update the lower bound.
for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++)
{
- BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier];
+ BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]);
+
+ BYTE * pBufRW = pBuf;
+ ExecutableWriterHolder barrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ barrierWriterHolder = ExecutableWriterHolder(pBuf, 42);
+ pBufRW = barrierWriterHolder.GetRW();
+ }
// assert there is in fact a cmp r/m32, imm32 there
_ASSERTE(pBuf[2] == 0x81);
// Update the immediate which is the lower bound of the ephemeral generation
- size_t *pfunc = (size_t *) &pBuf[AnyGrow_EphemeralLowerBound];
+ size_t *pfunc = (size_t *) &pBufRW[AnyGrow_EphemeralLowerBound];
//avoid trivial self modifying code
if (*pfunc != (size_t) g_ephemeral_low)
{
@@ -1214,7 +1230,7 @@ int StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */)
_ASSERTE(pBuf[10] == 0x81);
// Update the upper bound if we are using the PostGrow thunk.
- pfunc = (size_t *) &pBuf[PostGrow_EphemeralUpperBound];
+ pfunc = (size_t *) &pBufRW[PostGrow_EphemeralUpperBound];
//avoid trivial self modifying code
if (*pfunc != (size_t) g_ephemeral_high)
{
@@ -1244,7 +1260,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
#ifdef WRITE_BARRIER_CHECK
// Don't do the fancy optimization if we are checking write barrier
- if (((BYTE *)JIT_WriteBarrierEAX)[0] == 0xE9) // we are using slow write barrier
+ if ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[0] == 0xE9) // we are using slow write barrier
return stompWBCompleteActions;
#endif // WRITE_BARRIER_CHECK
@@ -1253,12 +1269,20 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
for (int iBarrier = 0; iBarrier < NUM_WRITE_BARRIERS; iBarrier++)
{
- BYTE * pBuf = (BYTE *)c_rgWriteBarriers[iBarrier];
+ BYTE * pBuf = GetWriteBarrierCodeLocation((BYTE *)c_rgWriteBarriers[iBarrier]);
int reg = c_rgWriteBarrierRegs[iBarrier];
size_t *pfunc;
- // Check if we are still using the pre-grow version of the write barrier.
+ BYTE * pBufRW = pBuf;
+ ExecutableWriterHolder barrierWriterHolder;
+ if (IsWriteBarrierCopyEnabled())
+ {
+ barrierWriterHolder = ExecutableWriterHolder(pBuf, 42);
+ pBufRW = barrierWriterHolder.GetRW();
+ }
+
+ // Check if we are still using the pre-grow version of the write barrier.
if (bWriteBarrierIsPreGrow)
{
// Check if we need to use the upper bounds checking barrier stub.
@@ -1271,7 +1295,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
}
pfunc = (size_t *) JIT_WriteBarrierReg_PostGrow;
- memcpy(pBuf, pfunc, 42);
+ memcpy(pBufRW, pfunc, 42);
// assert the copied code ends in a ret to make sure we got the right length
_ASSERTE(pBuf[41] == 0xC3);
@@ -1287,35 +1311,35 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
_ASSERTE(pBuf[0] == 0x89);
// Update the reg field (bits 3..5) of the ModR/M byte of this instruction
- pBuf[1] &= 0xc7;
- pBuf[1] |= reg << 3;
+ pBufRW[1] &= 0xc7;
+ pBufRW[1] |= reg << 3;
// Second instruction to patch is cmp reg, imm32 (low bound)
_ASSERTE(pBuf[2] == 0x81);
// Here the lowest three bits in ModR/M field are the register
- pBuf[3] &= 0xf8;
- pBuf[3] |= reg;
+ pBufRW[3] &= 0xf8;
+ pBufRW[3] |= reg;
// Third instruction to patch is another cmp reg, imm32 (high bound)
_ASSERTE(pBuf[10] == 0x81);
// Here the lowest three bits in ModR/M field are the register
- pBuf[11] &= 0xf8;
- pBuf[11] |= reg;
+ pBufRW[11] &= 0xf8;
+ pBufRW[11] |= reg;
bStompWriteBarrierEphemeral = true;
// What we're trying to update is the offset field of a
// cmp offset[edx], 0ffh instruction
_ASSERTE(pBuf[22] == 0x80);
- pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation];
+ pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation];
*pfunc = (size_t) g_card_table;
// What we're trying to update is the offset field of a
// mov offset[edx], 0ffh instruction
_ASSERTE(pBuf[34] == 0xC6);
- pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation];
+ pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation];
}
else
@@ -1324,14 +1348,14 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
// cmp offset[edx], 0ffh instruction
_ASSERTE(pBuf[14] == 0x80);
- pfunc = (size_t *) &pBuf[PreGrow_CardTableFirstLocation];
+ pfunc = (size_t *) &pBufRW[PreGrow_CardTableFirstLocation];
*pfunc = (size_t) g_card_table;
// What we're trying to update is the offset field of a
// mov offset[edx], 0ffh instruction
_ASSERTE(pBuf[26] == 0xC6);
- pfunc = (size_t *) &pBuf[PreGrow_CardTableSecondLocation];
+ pfunc = (size_t *) &pBufRW[PreGrow_CardTableSecondLocation];
}
}
else
@@ -1340,13 +1364,13 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
// cmp offset[edx], 0ffh instruction
_ASSERTE(pBuf[22] == 0x80);
- pfunc = (size_t *) &pBuf[PostGrow_CardTableFirstLocation];
+ pfunc = (size_t *) &pBufRW[PostGrow_CardTableFirstLocation];
*pfunc = (size_t) g_card_table;
// What we're trying to update is the offset field of a
// mov offset[edx], 0ffh instruction
_ASSERTE(pBuf[34] == 0xC6);
- pfunc = (size_t *) &pBuf[PostGrow_CardTableSecondLocation];
+ pfunc = (size_t *) &pBufRW[PostGrow_CardTableSecondLocation];
}
// Stick in the adjustment value.
diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp
index 61c5dfd90cbfc..564363053fc6a 100644
--- a/src/coreclr/vm/i386/stublinkerx86.cpp
+++ b/src/coreclr/vm/i386/stublinkerx86.cpp
@@ -4829,7 +4829,7 @@ VOID StubLinkerCPU::EmitArrayOpStub(const ArrayOpScript* pArrayOpScript)
X86EmitOp(0x8d, kEDX, elemBaseReg, elemOfs, elemScaledReg, elemScale);
// call JIT_Writeable_Thunks_Buf.WriteBarrierReg[0] (== EAX)
- X86EmitCall(NewExternalCodeLabel((LPVOID) &JIT_WriteBarrierEAX), 0);
+ X86EmitCall(NewExternalCodeLabel((LPVOID) GetWriteBarrierCodeLocation(&JIT_WriteBarrierEAX)), 0);
}
else
#else // TARGET_AMD64
diff --git a/src/coreclr/vm/i386/stublinkerx86.h b/src/coreclr/vm/i386/stublinkerx86.h
index af5244d077193..564c999975e7c 100644
--- a/src/coreclr/vm/i386/stublinkerx86.h
+++ b/src/coreclr/vm/i386/stublinkerx86.h
@@ -536,7 +536,7 @@ struct StubPrecode {
return rel32Decode(PTR_HOST_MEMBER_TADDR(StubPrecode, this, m_rel32));
}
-
+#ifndef DACCESS_COMPILE
void ResetTargetInterlocked()
{
CONTRACTL
@@ -562,6 +562,7 @@ struct StubPrecode {
ExecutableWriterHolder rel32Holder(&m_rel32, 4);
return rel32SetInterlocked(&m_rel32, rel32Holder.GetRW(), target, expected, (MethodDesc*)GetMethodDesc());
}
+#endif // !DACCESS_COMPILE
};
IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_movR10) == OFFSETOF_PRECODE_TYPE);)
IN_TARGET_64BIT(static_assert_no_msg(offsetof(StubPrecode, m_type) == OFFSETOF_PRECODE_TYPE_MOV_R10);)
@@ -646,6 +647,13 @@ struct FixupPrecode {
return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode);
}
+ size_t GetSizeRW()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return GetBase() + sizeof(void*) - dac_cast(this);
+ }
+
TADDR GetMethodDesc();
#else // HAS_FIXUP_PRECODE_CHUNKS
TADDR GetMethodDesc()
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index a1e4d93d881de..882e2c29cef04 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -11875,7 +11875,7 @@ WORD CEEJitInfo::getRelocTypeHint(void * target)
if (m_fAllowRel32)
{
// The JIT calls this method for data addresses only. It always uses REL32s for direct code targets.
- if (IsPreferredExecutableRange(target))
+ if (ExecutableAllocator::IsPreferredExecutableRange(target))
return IMAGE_REL_BASED_REL32;
}
#endif // TARGET_AMD64
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index ca9d03c2141d3..e071d0717d179 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -238,15 +238,10 @@ extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type,
extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj);
extern "C" FCDECL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj);
-#if defined(TARGET_ARM64) || defined(FEATURE_WRITEBARRIER_COPY)
// ARM64 JIT_WriteBarrier uses speciall ABI and thus is not callable directly
// Copied write barriers must be called at a different location
extern "C" FCDECL2(VOID, JIT_WriteBarrier_Callable, Object **dst, Object *ref);
#define WriteBarrier_Helper JIT_WriteBarrier_Callable
-#else
-// in other cases the regular JIT helper is callable.
-#define WriteBarrier_Helper JIT_WriteBarrier
-#endif
extern "C" FCDECL1(void, JIT_InternalThrow, unsigned exceptNum);
extern "C" FCDECL1(void*, JIT_InternalThrowFromHelper, unsigned exceptNum);
@@ -344,28 +339,25 @@ EXTERN_C FCDECL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift);
#ifdef TARGET_X86
+#define ENUM_X86_WRITE_BARRIER_REGISTERS() \
+ X86_WRITE_BARRIER_REGISTER(EAX) \
+ X86_WRITE_BARRIER_REGISTER(ECX) \
+ X86_WRITE_BARRIER_REGISTER(EBX) \
+ X86_WRITE_BARRIER_REGISTER(ESI) \
+ X86_WRITE_BARRIER_REGISTER(EDI) \
+ X86_WRITE_BARRIER_REGISTER(EBP)
+
extern "C"
{
- void STDCALL JIT_CheckedWriteBarrierEAX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_CheckedWriteBarrierEBX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_CheckedWriteBarrierECX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_CheckedWriteBarrierESI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_CheckedWriteBarrierEDI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_CheckedWriteBarrierEBP(); // JIThelp.asm/JIThelp.s
-
- void STDCALL JIT_DebugWriteBarrierEAX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_DebugWriteBarrierEBX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_DebugWriteBarrierECX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_DebugWriteBarrierESI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_DebugWriteBarrierEDI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_DebugWriteBarrierEBP(); // JIThelp.asm/JIThelp.s
-
- void STDCALL JIT_WriteBarrierEAX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_WriteBarrierEBX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_WriteBarrierECX(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_WriteBarrierESI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_WriteBarrierEDI(); // JIThelp.asm/JIThelp.s
- void STDCALL JIT_WriteBarrierEBP(); // JIThelp.asm/JIThelp.s
+
+// JIThelp.asm/JIThelp.s
+#define X86_WRITE_BARRIER_REGISTER(reg) \
+ void STDCALL JIT_CheckedWriteBarrier##reg(); \
+ void STDCALL JIT_DebugWriteBarrier##reg(); \
+ void STDCALL JIT_WriteBarrier##reg();
+
+ ENUM_X86_WRITE_BARRIER_REGISTERS()
+#undef X86_WRITE_BARRIER_REGISTER
void STDCALL JIT_WriteBarrierGroup();
void STDCALL JIT_WriteBarrierGroup_End();
diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp
index 4f222be4a2c03..0a77e4445f06f 100644
--- a/src/coreclr/vm/loaderallocator.cpp
+++ b/src/coreclr/vm/loaderallocator.cpp
@@ -1137,7 +1137,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory)
_ASSERTE(dwTotalReserveMemSize <= VIRTUAL_ALLOC_RESERVE_GRANULARITY);
#endif
- BYTE * initReservedMem = ClrVirtualAllocExecutable(dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS);
+ BYTE * initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize);
m_InitialReservedMemForLoaderHeaps = initReservedMem;
@@ -1672,18 +1672,25 @@ void AssemblyLoaderAllocator::SetCollectible()
{
CONTRACTL
{
- THROWS;
+ NOTHROW;
}
CONTRACTL_END;
m_IsCollectible = true;
-#ifndef DACCESS_COMPILE
- m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap);
-#endif
}
#ifndef DACCESS_COMPILE
+void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain)
+{
+ m_Id.Init();
+ LoaderAllocator::Init((BaseDomain *)pAppDomain);
+ if (IsCollectible())
+ {
+ m_pShuffleThunkCache = new ShuffleThunkCache(m_pStubHeap);
+ }
+}
+
#ifndef CROSSGEN_COMPILE
AssemblyLoaderAllocator::~AssemblyLoaderAllocator()
diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl
index a826675ccc93c..993732d4010f8 100644
--- a/src/coreclr/vm/loaderallocator.inl
+++ b/src/coreclr/vm/loaderallocator.inl
@@ -21,12 +21,6 @@ inline void GlobalLoaderAllocator::Init(BaseDomain *pDomain)
LoaderAllocator::Init(pDomain, m_ExecutableHeapInstance);
}
-inline void AssemblyLoaderAllocator::Init(AppDomain* pAppDomain)
-{
- m_Id.Init();
- LoaderAllocator::Init((BaseDomain *)pAppDomain);
-}
-
inline BOOL LoaderAllocatorID::Equals(LoaderAllocatorID *pId)
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index bd3984d8697cd..db308ab208a8e 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -4188,46 +4188,6 @@ c_CentralJumpCode = {
};
#include
-#elif defined(TARGET_AMD64)
-
-#include
-static const struct CentralJumpCode {
- BYTE m_movzxRAX[4];
- BYTE m_shlEAX[4];
- BYTE m_movRAX[2];
- MethodDesc* m_pBaseMD;
- BYTE m_addR10RAX[3];
- BYTE m_jmp[1];
- INT32 m_rel32;
-
- inline void Setup(CentralJumpCode* pCodeRX, MethodDesc* pMD, PCODE target, LoaderAllocator *pLoaderAllocator) {
- WRAPPER_NO_CONTRACT;
- m_pBaseMD = pMD;
- m_rel32 = rel32UsingJumpStub(&pCodeRX->m_rel32, target, pMD, pLoaderAllocator);
- }
-
- inline BOOL CheckTarget(TADDR target) {
- WRAPPER_NO_CONTRACT;
- TADDR addr = rel32Decode(PTR_HOST_MEMBER_TADDR(CentralJumpCode, this, m_rel32));
- if (*PTR_BYTE(addr) == 0x48 &&
- *PTR_BYTE(addr+1) == 0xB8 &&
- *PTR_BYTE(addr+10) == 0xFF &&
- *PTR_BYTE(addr+11) == 0xE0)
- {
- addr = *PTR_TADDR(addr+2);
- }
- return (addr == target);
- }
-}
-c_CentralJumpCode = {
- { 0x48, 0x0F, 0xB6, 0xC0 }, // movzx rax,al
- { 0x48, 0xC1, 0xE0, MethodDesc::ALIGNMENT_SHIFT }, // shl rax, MethodDesc::ALIGNMENT_SHIFT
- { 0x49, 0xBA }, NULL, // mov r10, pBaseMD
- { 0x4C, 0x03, 0xD0 }, // add r10,rax
- { 0xE9 }, 0 // jmp PreStub
-};
-#include
-
#elif defined(TARGET_ARM)
#include
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index 834d3cfc8c40b..8bdc5f0c2a892 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -1577,6 +1577,7 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT,
// Shortcut for generic approx type scenario
if (pMTInterfaceMapOwner != NULL &&
+ !pMTInterfaceMapOwner->ContainsGenericVariables() &&
IsSpecialMarkerTypeForGenericCasting() &&
GetTypeDefRid() == pTargetMT->GetTypeDefRid() &&
GetModule() == pTargetMT->GetModule() &&
@@ -1603,7 +1604,7 @@ BOOL MethodTable::CanCastByVarianceToInterfaceOrDelegate(MethodTable *pTargetMT,
for (DWORD i = 0; i < inst.GetNumArgs(); i++)
{
TypeHandle thArg = inst[i];
- if (IsSpecialMarkerTypeForGenericCasting() && pMTInterfaceMapOwner)
+ if (IsSpecialMarkerTypeForGenericCasting() && pMTInterfaceMapOwner && !pMTInterfaceMapOwner->ContainsGenericVariables())
{
thArg = pMTInterfaceMapOwner;
}
@@ -9820,7 +9821,7 @@ PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMT
CONTRACT_END;
MethodTable *pResult = m_pMap->GetMethodTable();
- if (pResult->IsSpecialMarkerTypeForGenericCasting())
+ if (pResult->IsSpecialMarkerTypeForGenericCasting() && !pMTOwner->ContainsGenericVariables())
{
TypeHandle ownerAsInst[MaxGenericParametersForSpecialMarkerType];
for (DWORD i = 0; i < MaxGenericParametersForSpecialMarkerType; i++)
diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h
index 4063e50b7b61c..df712a378d4ec 100644
--- a/src/coreclr/vm/methodtable.h
+++ b/src/coreclr/vm/methodtable.h
@@ -2245,7 +2245,8 @@ class MethodTable
{
if (pCurrentMethodTable->HasSameTypeDefAs(pMT) &&
pMT->HasInstantiation() &&
- pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() &&
+ pCurrentMethodTable->IsSpecialMarkerTypeForGenericCasting() &&
+ !pMTOwner->ContainsGenericVariables() &&
pMT->GetInstantiation().ContainsAllOneType(pMTOwner))
{
exactMatch = true;
diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl
index a3adc702cbe3d..b1af313a29556 100644
--- a/src/coreclr/vm/methodtable.inl
+++ b/src/coreclr/vm/methodtable.inl
@@ -1571,7 +1571,7 @@ FORCEINLINE BOOL MethodTable::ImplementsInterfaceInline(MethodTable *pInterface)
while (--numInterfaces);
// Second scan, looking for the curiously recurring generic scenario
- if (pInterface->HasInstantiation() && pInterface->GetInstantiation().ContainsAllOneType(this))
+ if (pInterface->HasInstantiation() && !ContainsGenericVariables() && pInterface->GetInstantiation().ContainsAllOneType(this))
{
numInterfaces = GetNumInterfaces();
pInfo = GetInterfaceMap();
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 89926ffdaae02..154c642cf40d7 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -9101,8 +9101,13 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT)
MethodTable **pExactMTs = (MethodTable**) _alloca(sizeof(MethodTable *) * nInterfacesCount);
BOOL duplicates;
bool retry = false;
- bool retryWithExactInterfaces = !pMT->IsValueType() || pMT->IsSharedByGenericInstantiations(); // Always use exact loading behavior with classes or shared generics, as they have to deal with inheritance, and the
- // inexact matching logic for classes would be more complex to write.
+
+ // Always use exact loading behavior with classes or shared generics, as they have to deal with inheritance, and the
+ // inexact matching logic for classes would be more complex to write.
+ // Also always use the exact loading behavior with any generic that contains generic variables, as the open type is used
+ // to represent a type instantiated over its own generic variables, and the special marker type is currently the open type
+ // and we make this case distinguishable by simply disallowing the optimization in those cases.
+ bool retryWithExactInterfaces = !pMT->IsValueType() || pMT->IsSharedByGenericInstantiations() || pMT->ContainsGenericVariables();
DWORD nAssigned = 0;
do
@@ -9132,7 +9137,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT)
(const Substitution*)0,
retryWithExactInterfaces ? NULL : pMT).GetMethodTable();
- bool uninstGenericCase = pNewIntfMT->IsSpecialMarkerTypeForGenericCasting();
+ bool uninstGenericCase = !retryWithExactInterfaces && pNewIntfMT->IsSpecialMarkerTypeForGenericCasting();
duplicates |= InsertMethodTable(pNewIntfMT, pExactMTs, nInterfacesCount, &nAssigned);
diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp
index 80731c191e737..0bd2bd657f9ad 100644
--- a/src/coreclr/vm/precode.cpp
+++ b/src/coreclr/vm/precode.cpp
@@ -480,7 +480,9 @@ void Precode::Reset()
#ifdef HAS_FIXUP_PRECODE_CHUNKS
if (t == PRECODE_FIXUP)
{
- size = sizeof(FixupPrecode) + sizeof(PTR_MethodDesc);
+ // The writeable size the Init method accesses is dynamic depending on
+ // the FixupPrecode members.
+ size = ((FixupPrecode*)this)->GetSizeRW();
}
else
#endif
diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp
index 0971334af4d31..e61802b984950 100644
--- a/src/coreclr/vm/stackwalk.cpp
+++ b/src/coreclr/vm/stackwalk.cpp
@@ -713,14 +713,12 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
// get our caller's PSP, or our caller's caller's SP.
while (!ExecutionManager::IsManagedCode(uControlPc))
{
-#ifdef FEATURE_WRITEBARRIER_COPY
if (IsIPInWriteBarrierCodeCopy(uControlPc))
{
// Pretend we were executing the barrier function at its original location so that the unwinder can unwind the frame
uControlPc = AdjustWriteBarrierIP(uControlPc);
SetIP(pContext, uControlPc);
}
-#endif // FEATURE_WRITEBARRIER_COPY
#ifndef TARGET_UNIX
uControlPc = VirtualUnwindCallFrame(pContext);
diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp
index 04a33e3982613..304cb4fb35b44 100644
--- a/src/coreclr/vm/stublink.cpp
+++ b/src/coreclr/vm/stublink.cpp
@@ -846,7 +846,7 @@ Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags)
);
ASSERT(pStub != NULL);
- bool fSuccess = EmitStub(pStub, globalsize, pHeap);
+ bool fSuccess = EmitStub(pStub, globalsize, size, pHeap);
#ifdef STUBLINKER_GENERATES_UNWIND_INFO
if (fSuccess)
@@ -1007,13 +1007,13 @@ int StubLinker::CalculateSize(int* pGlobalSize)
return globalsize + datasize;
}
-bool StubLinker::EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap)
+bool StubLinker::EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap)
{
STANDARD_VM_CONTRACT;
BYTE *pCode = (BYTE*)(pStub->GetBlob());
- ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub));
+ ExecutableWriterHolder stubWriterHolder(pStub, sizeof(Stub) + totalSize);
Stub *pStubRW = stubWriterHolder.GetRW();
BYTE *pCodeRW = (BYTE*)(pStubRW->GetBlob());
@@ -2013,11 +2013,7 @@ VOID Stub::DeleteStub()
FillMemory(this+1, m_numCodeBytes, 0xcc);
#endif
-#ifndef TARGET_UNIX
- DeleteExecutable((BYTE*)GetAllocationBase());
-#else
delete [] (BYTE*)GetAllocationBase();
-#endif
}
}
@@ -2124,11 +2120,7 @@ Stub* Stub::NewStub(PTR_VOID pCode, DWORD flags)
BYTE *pBlock;
if (pHeap == NULL)
{
-#ifndef TARGET_UNIX
- pBlock = new (executable) BYTE[totalSize];
-#else
pBlock = new BYTE[totalSize];
-#endif
}
else
{
diff --git a/src/coreclr/vm/stublink.h b/src/coreclr/vm/stublink.h
index 94326f9962ea7..9613fd48f687d 100644
--- a/src/coreclr/vm/stublink.h
+++ b/src/coreclr/vm/stublink.h
@@ -395,7 +395,7 @@ class StubLinker
// Writes out the code element into memory following the
// stub object.
- bool EmitStub(Stub* pStub, int globalsize, LoaderHeap* pHeap);
+ bool EmitStub(Stub* pStub, int globalsize, int totalSize, LoaderHeap* pHeap);
CodeRun *GetLastCodeRunIfAny();
diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp
index fa93110399d39..c6485b86d59c7 100644
--- a/src/coreclr/vm/threads.cpp
+++ b/src/coreclr/vm/threads.cpp
@@ -1078,18 +1078,30 @@ DWORD_PTR Thread::OBJREF_HASH = OBJREF_TABSIZE;
extern "C" void STDCALL JIT_PatchedCodeStart();
extern "C" void STDCALL JIT_PatchedCodeLast();
-#ifdef FEATURE_WRITEBARRIER_COPY
-
static void* s_barrierCopy = NULL;
BYTE* GetWriteBarrierCodeLocation(VOID* barrier)
{
- return (BYTE*)s_barrierCopy + ((BYTE*)barrier - (BYTE*)JIT_PatchedCodeStart);
+ if (IsWriteBarrierCopyEnabled())
+ {
+ return (BYTE*)PINSTRToPCODE((TADDR)s_barrierCopy + ((TADDR)barrier - (TADDR)JIT_PatchedCodeStart));
+ }
+ else
+ {
+ return (BYTE*)barrier;
+ }
}
BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc)
{
- return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart)));
+ if (IsWriteBarrierCopyEnabled())
+ {
+ return (s_barrierCopy <= (void*)controlPc && (void*)controlPc < ((BYTE*)s_barrierCopy + ((BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart)));
+ }
+ else
+ {
+ return FALSE;
+ }
}
PCODE AdjustWriteBarrierIP(PCODE controlPc)
@@ -1100,14 +1112,21 @@ PCODE AdjustWriteBarrierIP(PCODE controlPc)
return (PCODE)JIT_PatchedCodeStart + (controlPc - (PCODE)s_barrierCopy);
}
+#ifdef TARGET_X86
+extern "C" void *JIT_WriteBarrierEAX_Loc;
+#else
extern "C" void *JIT_WriteBarrier_Loc;
+#endif
+
#ifdef TARGET_ARM64
extern "C" void (*JIT_WriteBarrier_Table)();
extern "C" void *JIT_WriteBarrier_Loc = 0;
extern "C" void *JIT_WriteBarrier_Table_Loc = 0;
#endif // TARGET_ARM64
-#endif // FEATURE_WRITEBARRIER_COPY
+#ifdef TARGET_ARM
+extern "C" void *JIT_WriteBarrier_Loc = 0;
+#endif // TARGET_ARM
#ifndef TARGET_UNIX
// g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out.
@@ -1131,57 +1150,85 @@ void InitThreadManager()
}
CONTRACTL_END;
- InitializeYieldProcessorNormalizedCrst();
-
// All patched helpers should fit into one page.
// If you hit this assert on retail build, there is most likely problem with BBT script.
_ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart > (ptrdiff_t)0);
_ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart < (ptrdiff_t)GetOsPageSize());
-#ifdef FEATURE_WRITEBARRIER_COPY
- s_barrierCopy = ClrVirtualAlloc(NULL, g_SystemInfo.dwAllocationGranularity, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- if (s_barrierCopy == NULL)
+ if (IsWriteBarrierCopyEnabled())
{
- _ASSERTE(!"ClrVirtualAlloc of GC barrier code page failed");
- COMPlusThrowWin32();
- }
+ s_barrierCopy = ExecutableAllocator::Instance()->Reserve(g_SystemInfo.dwAllocationGranularity);
+ ExecutableAllocator::Instance()->Commit(s_barrierCopy, g_SystemInfo.dwAllocationGranularity, true);
+ if (s_barrierCopy == NULL)
+ {
+ _ASSERTE(!"Allocation of GC barrier code page failed");
+ COMPlusThrowWin32();
+ }
- {
- size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart;
- ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize);
- memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize);
- }
+ {
+ size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart;
+ ExecutableWriterHolder barrierWriterHolder(s_barrierCopy, writeBarrierSize);
+ memcpy(barrierWriterHolder.GetRW(), (BYTE*)JIT_PatchedCodeStart, writeBarrierSize);
+ }
- // Store the JIT_WriteBarrier copy location to a global variable so that helpers
- // can jump to it.
- JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier);
+ // Store the JIT_WriteBarrier copy location to a global variable so that helpers
+ // can jump to it.
+#ifdef TARGET_X86
+ JIT_WriteBarrierEAX_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrierEAX);
- SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier));
+#define X86_WRITE_BARRIER_REGISTER(reg) \
+ SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF_##reg, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg)); \
+ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("@WriteBarrier" #reg));
-#ifdef TARGET_ARM64
- // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated.
- JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table);
+ ENUM_X86_WRITE_BARRIER_REGISTERS()
- SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier));
- SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier));
-#endif // TARGET_ARM64
+#undef X86_WRITE_BARRIER_REGISTER
-#else // FEATURE_WRITEBARRIER_COPY
+#else // TARGET_X86
+ JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier);
+#endif // TARGET_X86
+ SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier));
+ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier"));
- // I am using virtual protect to cover the entire range that this code falls in.
- //
+#ifdef TARGET_ARM64
+ // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated.
+ JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table);
+#endif // TARGET_ARM64
- // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth,
- // so instead we'll leave it writable from here forward.
+#if defined(TARGET_ARM64) || defined(TARGET_ARM)
+ SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier));
+ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier"));
+ SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier));
+ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier"));
+#endif // TARGET_ARM64 || TARGET_ARM
- DWORD oldProt;
- if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart,
- PAGE_EXECUTE_READWRITE, &oldProt))
+ }
+ else
{
- _ASSERTE(!"ClrVirtualProtect of code page failed");
- COMPlusThrowWin32();
+ // I am using virtual protect to cover the entire range that this code falls in.
+ //
+
+ // We could reset it to non-writeable inbetween GCs and such, but then we'd have to keep on re-writing back and forth,
+ // so instead we'll leave it writable from here forward.
+
+ DWORD oldProt;
+ if (!ClrVirtualProtect((void *)JIT_PatchedCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart,
+ PAGE_EXECUTE_READWRITE, &oldProt))
+ {
+ _ASSERTE(!"ClrVirtualProtect of code page failed");
+ COMPlusThrowWin32();
+ }
+
+#ifdef TARGET_X86
+ JIT_WriteBarrierEAX_Loc = (void*)JIT_WriteBarrierEAX;
+#else
+ JIT_WriteBarrier_Loc = (void*)JIT_WriteBarrier;
+#endif
+#ifdef TARGET_ARM64
+ // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated.
+ JIT_WriteBarrier_Table_Loc = (void*)&JIT_WriteBarrier_Table;
+#endif // TARGET_ARM64
}
-#endif // FEATURE_WRITEBARRIER_COPY
#ifndef TARGET_UNIX
_ASSERTE(GetThreadNULLOk() == NULL);
@@ -7145,6 +7192,7 @@ BOOL Thread::HaveExtraWorkForFinalizer()
|| Thread::CleanupNeededForFinalizedThread()
|| (m_DetachCount > 0)
|| SystemDomain::System()->RequireAppDomainCleanup()
+ || YieldProcessorNormalization::IsMeasurementScheduled()
|| ThreadStore::s_pThreadStore->ShouldTriggerGCForDeadThreads();
}
@@ -7191,6 +7239,12 @@ void Thread::DoExtraWorkForFinalizer()
// If there were any TimerInfos waiting to be released, they'll get flushed now
ThreadpoolMgr::FlushQueueOfTimerInfos();
+ if (YieldProcessorNormalization::IsMeasurementScheduled())
+ {
+ GCX_PREEMP();
+ YieldProcessorNormalization::PerformMeasurement();
+ }
+
ThreadStore::s_pThreadStore->TriggerGCForDeadThreadsIfNecessary();
}
diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h
index d18b21d58f95a..7d600dab5edac 100644
--- a/src/coreclr/vm/threads.h
+++ b/src/coreclr/vm/threads.h
@@ -6271,18 +6271,23 @@ class ThreadStateNCStackHolder
BOOL Debug_IsLockedViaThreadSuspension();
-#ifdef FEATURE_WRITEBARRIER_COPY
+inline BOOL IsWriteBarrierCopyEnabled()
+{
+#ifdef DACCESS_COMPILE
+ return FALSE;
+#else // DACCESS_COMPILE
+#ifdef HOST_OSX
+ return TRUE;
+#else
+ return ExecutableAllocator::IsWXORXEnabled();
+#endif
+#endif // DACCESS_COMPILE
+}
BYTE* GetWriteBarrierCodeLocation(VOID* barrier);
BOOL IsIPInWriteBarrierCodeCopy(PCODE controlPc);
PCODE AdjustWriteBarrierIP(PCODE controlPc);
-#else // FEATURE_WRITEBARRIER_COPY
-
-#define GetWriteBarrierCodeLocation(barrier) ((BYTE*)(barrier))
-
-#endif // FEATURE_WRITEBARRIER_COPY
-
#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
extern thread_local Thread* t_pStackWalkerWalkingThread;
#define SET_THREAD_TYPE_STACKWALKER(pThread) t_pStackWalkerWalkingThread = pThread
diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp
index 2644d7ad5fc91..bac0f082fb8ed 100644
--- a/src/coreclr/vm/threadstatics.cpp
+++ b/src/coreclr/vm/threadstatics.cpp
@@ -97,7 +97,7 @@ void ThreadLocalBlock::FreeTable()
SpinLock::Holder lock(&m_TLMTableLock);
// Free the table itself
- delete m_pTLMTable;
+ delete[] m_pTLMTable;
m_pTLMTable = NULL;
}
@@ -136,7 +136,7 @@ void ThreadLocalBlock::EnsureModuleIndex(ModuleIndex index)
// If this allocation fails, we will throw. If it succeeds,
// then we are good to go
- PTR_TLMTableEntry pNewModuleSlots = (PTR_TLMTableEntry) (void*) new BYTE[sizeof(TLMTableEntry) * aModuleIndices];
+ PTR_TLMTableEntry pNewModuleSlots = new TLMTableEntry[aModuleIndices];
// Zero out the new TLM table
memset(pNewModuleSlots, 0 , sizeof(TLMTableEntry) * aModuleIndices);
@@ -704,9 +704,7 @@ PTR_ThreadLocalModule ThreadStatics::AllocateTLM(Module * pModule)
SIZE_T size = pModule->GetThreadLocalModuleSize();
- _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob());
-
- PTR_ThreadLocalModule pThreadLocalModule = (ThreadLocalModule*)new BYTE[size];
+ PTR_ThreadLocalModule pThreadLocalModule = new({ pModule }) ThreadLocalModule;
// We guarantee alignment for 64-bit regular thread statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons.
diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h
index 1755bf7230d20..ddb59b5cbc20d 100644
--- a/src/coreclr/vm/threadstatics.h
+++ b/src/coreclr/vm/threadstatics.h
@@ -450,6 +450,20 @@ struct ThreadLocalModule
return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG;
}
+ void* operator new(size_t) = delete;
+
+ struct ParentModule { PTR_Module pModule; };
+
+ void* operator new(size_t baseSize, ParentModule parentModule)
+ {
+ size_t size = parentModule.pModule->GetThreadLocalModuleSize();
+
+ _ASSERTE(size >= baseSize);
+ _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob());
+
+ return ::operator new(size);
+ }
+
#ifndef DACCESS_COMPILE
FORCEINLINE void EnsureClassAllocated(MethodTable * pMT)
diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp
index 95d568d641c73..3af4c52afc9bb 100644
--- a/src/coreclr/vm/virtualcallstub.cpp
+++ b/src/coreclr/vm/virtualcallstub.cpp
@@ -641,7 +641,7 @@ void VirtualCallStubManager::Init(BaseDomain *pDomain, LoaderAllocator *pLoaderA
dwTotalReserveMemSize);
}
- initReservedMem = ClrVirtualAllocExecutable (dwTotalReserveMemSize, MEM_RESERVE, PAGE_NOACCESS);
+ initReservedMem = (BYTE*)ExecutableAllocator::Instance()->Reserve(dwTotalReserveMemSize);
m_initialReservedMemForHeaps = (BYTE *) initReservedMem;
@@ -2766,11 +2766,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad
}
#endif
- ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder)
-#ifdef TARGET_AMD64
- + sizeof(DispatchStubShort)
-#endif
- );
+ ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize);
dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode,
addrOfFail,
(size_t)pMTExpected
@@ -2833,9 +2829,9 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE
} CONTRACT_END;
//allocate from the requisite heap and copy the template over it.
- DispatchHolder * holder = (DispatchHolder*) (void*)
- dispatch_heap->AllocAlignedMem(DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG), CODE_SIZE_ALIGN);
- ExecutableWriterHolder dispatchWriterHolder(holder, sizeof(DispatchHolder) + sizeof(DispatchStubLong));
+ size_t dispatchHolderSize = DispatchHolder::GetHolderSize(DispatchStub::e_TYPE_LONG);
+ DispatchHolder * holder = (DispatchHolder*) (void*)dispatch_heap->AllocAlignedMem(dispatchHolderSize, CODE_SIZE_ALIGN);
+ ExecutableWriterHolder dispatchWriterHolder(holder, dispatchHolderSize);
dispatchWriterHolder.GetRW()->Initialize(holder, addrOfCode,
addrOfFail,
diff --git a/src/coreclr/vm/yieldprocessornormalized.cpp b/src/coreclr/vm/yieldprocessornormalized.cpp
index 91547923310fb..2c51e73b678d8 100644
--- a/src/coreclr/vm/yieldprocessornormalized.cpp
+++ b/src/coreclr/vm/yieldprocessornormalized.cpp
@@ -2,17 +2,33 @@
// The .NET Foundation licenses this file to you under the MIT license.
#include "common.h"
+#include "yieldprocessornormalized.h"
-static Volatile s_isYieldProcessorNormalizedInitialized = false;
-static CrstStatic s_initializeYieldProcessorNormalizedCrst;
+#ifndef CROSSGEN_COMPILE
-void InitializeYieldProcessorNormalizedCrst()
+#include "finalizerthread.h"
+
+enum class NormalizationState : UINT8
{
- WRAPPER_NO_CONTRACT;
- s_initializeYieldProcessorNormalizedCrst.Init(CrstLeafLock);
-}
+ Uninitialized,
+ Initialized,
+ Failed
+};
+
+static const int NsPerYieldMeasurementCount = 8;
+static const unsigned int MeasurementPeriodMs = 4000;
+
+static const unsigned int NsPerS = 1000 * 1000 * 1000;
+
+static NormalizationState s_normalizationState = NormalizationState::Uninitialized;
+static unsigned int s_previousNormalizationTimeMs;
+
+static UINT64 s_performanceCounterTicksPerS;
+static double s_nsPerYieldMeasurements[NsPerYieldMeasurementCount];
+static int s_nextMeasurementIndex;
+static double s_establishedNsPerYield = YieldProcessorNormalization::TargetNsPerNormalizedYield;
-static void InitializeYieldProcessorNormalized()
+static unsigned int DetermineMeasureDurationUs()
{
CONTRACTL
{
@@ -22,92 +38,271 @@ static void InitializeYieldProcessorNormalized()
}
CONTRACTL_END;
- CrstHolder lock(&s_initializeYieldProcessorNormalizedCrst);
+ _ASSERTE(s_normalizationState != NormalizationState::Failed);
- if (s_isYieldProcessorNormalizedInitialized)
+ // On some systems, querying the high performance counter has relatively significant overhead. Increase the measure duration
+ // if the overhead seems high relative to the measure duration.
+ unsigned int measureDurationUs = 1;
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ UINT64 startTicks = li.QuadPart;
+ QueryPerformanceCounter(&li);
+ UINT64 elapsedTicks = li.QuadPart - startTicks;
+ if (elapsedTicks >= s_performanceCounterTicksPerS * measureDurationUs * (1000 / 4) / NsPerS) // elapsed >= 1/4 of the measure duration
{
- return;
+ measureDurationUs *= 4;
+ }
+ return measureDurationUs;
+}
+
+static double MeasureNsPerYield(unsigned int measureDurationUs)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
}
+ CONTRACTL_END;
- // Intel pre-Skylake processor: measured typically 14-17 cycles per yield
- // Intel post-Skylake processor: measured typically 125-150 cycles per yield
- const int MeasureDurationMs = 10;
- const int NsPerSecond = 1000 * 1000 * 1000;
+ _ASSERTE(s_normalizationState != NormalizationState::Failed);
+
+ int yieldCount = (int)(measureDurationUs * 1000 / s_establishedNsPerYield) + 1;
+ UINT64 ticksPerS = s_performanceCounterTicksPerS;
+ UINT64 measureDurationTicks = ticksPerS * measureDurationUs / (1000 * 1000);
LARGE_INTEGER li;
- if (!QueryPerformanceFrequency(&li) || (ULONGLONG)li.QuadPart < 1000 / MeasureDurationMs)
+ QueryPerformanceCounter(&li);
+ UINT64 startTicks = li.QuadPart;
+
+ for (int i = 0; i < yieldCount; ++i)
{
- // High precision clock not available or clock resolution is too low, resort to defaults
- s_isYieldProcessorNormalizedInitialized = true;
- return;
+ System_YieldProcessor();
}
- ULONGLONG ticksPerSecond = li.QuadPart;
- // Measure the nanosecond delay per yield
- ULONGLONG measureDurationTicks = ticksPerSecond / (1000 / MeasureDurationMs);
- unsigned int yieldCount = 0;
QueryPerformanceCounter(&li);
- ULONGLONG startTicks = li.QuadPart;
- ULONGLONG elapsedTicks;
- do
- {
- // On some systems, querying the high performance counter has relatively significant overhead. Do enough yields to mask
- // the timing overhead. Assuming one yield has a delay of MinNsPerNormalizedYield, 1000 yields would have a delay in the
- // low microsecond range.
- for (int i = 0; i < 1000; ++i)
+ UINT64 elapsedTicks = li.QuadPart - startTicks;
+ while (elapsedTicks < measureDurationTicks)
+ {
+ int nextYieldCount =
+ Max(4,
+ elapsedTicks == 0
+ ? yieldCount / 4
+ : (int)(yieldCount * (measureDurationTicks - elapsedTicks) / (double)elapsedTicks) + 1);
+ for (int i = 0; i < nextYieldCount; ++i)
{
System_YieldProcessor();
}
- yieldCount += 1000;
QueryPerformanceCounter(&li);
- ULONGLONG nowTicks = li.QuadPart;
- elapsedTicks = nowTicks - startTicks;
- } while (elapsedTicks < measureDurationTicks);
- double nsPerYield = (double)elapsedTicks * NsPerSecond / ((double)yieldCount * ticksPerSecond);
- if (nsPerYield < 1)
+ elapsedTicks = li.QuadPart - startTicks;
+ yieldCount += nextYieldCount;
+ }
+
+ // Limit the minimum to a reasonable value considering that on some systems a yield may be implemented as a no-op
+ const double MinNsPerYield = 0.1;
+
+ // Measured values higher than this don't affect values calculated for normalization, and it's very unlikely for a yield to
+ // really take this long. Limit the maximum to keep the recorded values reasonable.
+ const double MaxNsPerYield = YieldProcessorNormalization::TargetMaxNsPerSpinIteration / 1.5 + 1;
+
+ return Max(MinNsPerYield, Min((double)elapsedTicks * NsPerS / ((double)yieldCount * ticksPerS), MaxNsPerYield));
+}
+
+void YieldProcessorNormalization::PerformMeasurement()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(s_isMeasurementScheduled);
+
+ double latestNsPerYield;
+ if (s_normalizationState == NormalizationState::Initialized)
{
- nsPerYield = 1;
+ if (GetTickCount() - s_previousNormalizationTimeMs < MeasurementPeriodMs)
+ {
+ return;
+ }
+
+ int nextMeasurementIndex = s_nextMeasurementIndex;
+ latestNsPerYield = MeasureNsPerYield(DetermineMeasureDurationUs());
+ AtomicStore(&s_nsPerYieldMeasurements[nextMeasurementIndex], latestNsPerYield);
+ if (++nextMeasurementIndex >= NsPerYieldMeasurementCount)
+ {
+ nextMeasurementIndex = 0;
+ }
+ s_nextMeasurementIndex = nextMeasurementIndex;
}
+ else if (s_normalizationState == NormalizationState::Uninitialized)
+ {
+ LARGE_INTEGER li;
+ if (!QueryPerformanceFrequency(&li) || li.QuadPart < 1000 * 1000)
+ {
+ // High precision clock not available or clock resolution is too low, resort to defaults
+ s_normalizationState = NormalizationState::Failed;
+ return;
+ }
+ s_performanceCounterTicksPerS = li.QuadPart;
+
+ unsigned int measureDurationUs = DetermineMeasureDurationUs();
+ for (int i = 0; i < NsPerYieldMeasurementCount; ++i)
+ {
+ latestNsPerYield = MeasureNsPerYield(measureDurationUs);
+ AtomicStore(&s_nsPerYieldMeasurements[i], latestNsPerYield);
+ if (i == 0 || latestNsPerYield < s_establishedNsPerYield)
+ {
+ AtomicStore(&s_establishedNsPerYield, latestNsPerYield);
+ }
- // Calculate the number of yields required to span the duration of a normalized yield. Since nsPerYield is at least 1, this
- // value is naturally limited to MinNsPerNormalizedYield.
- int yieldsPerNormalizedYield = (int)(MinNsPerNormalizedYield / nsPerYield + 0.5);
- if (yieldsPerNormalizedYield < 1)
+ if (i < NsPerYieldMeasurementCount - 1)
+ {
+ FireEtwYieldProcessorMeasurement(GetClrInstanceId(), latestNsPerYield, s_establishedNsPerYield);
+ }
+ }
+ }
+ else
{
- yieldsPerNormalizedYield = 1;
+ _ASSERTE(s_normalizationState == NormalizationState::Failed);
+ return;
}
- _ASSERTE(yieldsPerNormalizedYield <= (int)MinNsPerNormalizedYield);
+
+ double establishedNsPerYield = s_nsPerYieldMeasurements[0];
+ for (int i = 1; i < NsPerYieldMeasurementCount; ++i)
+ {
+ double nsPerYield = s_nsPerYieldMeasurements[i];
+ if (nsPerYield < establishedNsPerYield)
+ {
+ establishedNsPerYield = nsPerYield;
+ }
+ }
+ if (establishedNsPerYield != s_establishedNsPerYield)
+ {
+ AtomicStore(&s_establishedNsPerYield, establishedNsPerYield);
+ }
+
+ FireEtwYieldProcessorMeasurement(GetClrInstanceId(), latestNsPerYield, s_establishedNsPerYield);
+
+ // Calculate the number of yields required to span the duration of a normalized yield
+ unsigned int yieldsPerNormalizedYield = Max(1u, (unsigned int)(TargetNsPerNormalizedYield / establishedNsPerYield + 0.5));
+ _ASSERTE(yieldsPerNormalizedYield <= MaxYieldsPerNormalizedYield);
+ s_yieldsPerNormalizedYield = yieldsPerNormalizedYield;
// Calculate the maximum number of yields that would be optimal for a late spin iteration. Typically, we would not want to
// spend excessive amounts of time (thousands of cycles) doing only YieldProcessor, as SwitchToThread/Sleep would do a
// better job of allowing other work to run.
- int optimalMaxNormalizedYieldsPerSpinIteration =
- (int)(NsPerOptimalMaxSpinIterationDuration / (yieldsPerNormalizedYield * nsPerYield) + 0.5);
- if (optimalMaxNormalizedYieldsPerSpinIteration < 1)
+ s_optimalMaxNormalizedYieldsPerSpinIteration =
+ Max(1u, (unsigned int)(TargetMaxNsPerSpinIteration / (yieldsPerNormalizedYield * establishedNsPerYield) + 0.5));
+ _ASSERTE(s_optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration);
+
+ GCHeapUtilities::GetGCHeap()->SetYieldProcessorScalingFactor((float)yieldsPerNormalizedYield);
+
+ s_previousNormalizationTimeMs = GetTickCount();
+ s_normalizationState = NormalizationState::Initialized;
+ s_isMeasurementScheduled = false;
+}
+
+#endif // !CROSSGEN_COMPILE
+
+void YieldProcessorNormalization::ScheduleMeasurementIfNecessary()
+{
+ CONTRACTL
{
- optimalMaxNormalizedYieldsPerSpinIteration = 1;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
}
+ CONTRACTL_END;
- g_yieldsPerNormalizedYield = yieldsPerNormalizedYield;
- g_optimalMaxNormalizedYieldsPerSpinIteration = optimalMaxNormalizedYieldsPerSpinIteration;
- s_isYieldProcessorNormalizedInitialized = true;
+#ifndef CROSSGEN_COMPILE
+ NormalizationState normalizationState = VolatileLoadWithoutBarrier(&s_normalizationState);
+ if (normalizationState == NormalizationState::Initialized)
+ {
+ if (GetTickCount() - s_previousNormalizationTimeMs < MeasurementPeriodMs)
+ {
+ return;
+ }
+ }
+ else if (normalizationState == NormalizationState::Uninitialized)
+ {
+ }
+ else
+ {
+ _ASSERTE(normalizationState == NormalizationState::Failed);
+ return;
+ }
- GCHeapUtilities::GetGCHeap()->SetYieldProcessorScalingFactor((float)yieldsPerNormalizedYield);
+ // !g_fEEStarted is required for FinalizerThread::EnableFinalization() below
+ if (s_isMeasurementScheduled || !g_fEEStarted)
+ {
+ return;
+ }
+
+ s_isMeasurementScheduled = true;
+ FinalizerThread::EnableFinalization();
+#endif // !CROSSGEN_COMPILE
}
-void EnsureYieldProcessorNormalizedInitialized()
+#ifndef CROSSGEN_COMPILE
+
+void YieldProcessorNormalization::FireMeasurementEvents()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
- MODE_PREEMPTIVE;
+ MODE_ANY;
}
CONTRACTL_END;
- if (!s_isYieldProcessorNormalizedInitialized)
+ if (!EventEnabledYieldProcessorMeasurement())
{
- InitializeYieldProcessorNormalized();
+ return;
}
+
+ // This function may be called at any time to fire events about recorded measurements. There is no synchronization for the
+ // recorded information, so try to enumerate the array with some care.
+ double establishedNsPerYield = AtomicLoad(&s_establishedNsPerYield);
+ int nextIndex = VolatileLoadWithoutBarrier(&s_nextMeasurementIndex);
+ for (int i = 0; i < NsPerYieldMeasurementCount; ++i)
+ {
+ double nsPerYield = AtomicLoad(&s_nsPerYieldMeasurements[nextIndex]);
+ if (nsPerYield != 0) // the array may not be fully initialized yet
+ {
+ FireEtwYieldProcessorMeasurement(GetClrInstanceId(), nsPerYield, establishedNsPerYield);
+ }
+
+ if (++nextIndex >= NsPerYieldMeasurementCount)
+ {
+ nextIndex = 0;
+ }
+ }
+}
+
+double YieldProcessorNormalization::AtomicLoad(double *valueRef)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifdef TARGET_64BIT
+ return VolatileLoadWithoutBarrier(valueRef);
+#else
+ return InterlockedCompareExchangeT(valueRef, 0.0, 0.0);
+#endif
}
+
+void YieldProcessorNormalization::AtomicStore(double *valueRef, double value)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifdef TARGET_64BIT
+ *valueRef = value;
+#else
+ InterlockedExchangeT(valueRef, value);
+#endif
+}
+
+#endif // !CROSSGEN_COMPILE
diff --git a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs b/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs
index d22677aa617d8..96fa3b2adbad2 100644
--- a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs
+++ b/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs
@@ -14,7 +14,14 @@ public static void Main(string[] args)
foreach (string propertyName in args)
{
- Console.WriteLine($"AppContext.GetData({propertyName}) = {System.AppContext.GetData(propertyName)}");
+ var propertyValue = (string)System.AppContext.GetData(propertyName);
+ if (string.IsNullOrEmpty(propertyValue))
+ {
+ Console.WriteLine($"Property '{propertyName}' was not found.");
+ continue;
+ }
+
+ Console.WriteLine($"AppContext.GetData({propertyName}) = {propertyValue}");
}
}
}
diff --git a/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs b/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs
index 2a99ab5681f75..0a8d54559e227 100644
--- a/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs
+++ b/src/installer/tests/HostActivation.Tests/DotNetBuilder.cs
@@ -174,6 +174,22 @@ public DotNetBuilder AddFramework(
return this;
}
+ public DotNetBuilder AddMockSDK(
+ string version,
+ string MNAVersion)
+ {
+ string path = Path.Combine(_path, "sdk", version);
+ Directory.CreateDirectory(path);
+
+ using var _ = File.Create(Path.Combine(path, "dotnet.dll"));
+
+ RuntimeConfig dotnetRuntimeConfig = new RuntimeConfig(Path.Combine(path, "dotnet.runtimeconfig.json"));
+ dotnetRuntimeConfig.WithFramework(new RuntimeConfig.Framework("Microsoft.NETCore.App", MNAVersion));
+ dotnetRuntimeConfig.Save();
+
+ return this;
+ }
+
public DotNetCli Build()
{
return new DotNetCli(_path);
diff --git a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs
index 6d8949c4132a5..01b5b415ecd26 100644
--- a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs
+++ b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
+using Microsoft.DotNet.Cli.Build;
using Xunit;
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
@@ -25,9 +26,7 @@ public void AppConfigProperty_AppCanGetData()
var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;
dotnet.Exec(appDll, sharedState.AppTestPropertyName)
- .EnvironmentVariable("COREHOST_TRACE", "1")
- .CaptureStdErr()
- .CaptureStdOut()
+ .EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {sharedState.AppTestPropertyName} = {sharedState.AppTestPropertyValue}")
@@ -43,9 +42,7 @@ public void FrameworkConfigProperty_AppCanGetData()
var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;
dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName)
- .EnvironmentVariable("COREHOST_TRACE", "1")
- .CaptureStdErr()
- .CaptureStdOut()
+ .EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.FrameworkTestPropertyValue}")
@@ -65,15 +62,39 @@ public void DuplicateConfigProperty_AppConfigValueUsed()
var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;
dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName)
- .EnvironmentVariable("COREHOST_TRACE", "1")
- .CaptureStdErr()
- .CaptureStdOut()
+ .EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.AppTestPropertyValue}")
.And.HaveStdOutContaining($"AppContext.GetData({sharedState.FrameworkTestPropertyName}) = {sharedState.AppTestPropertyValue}");
}
+ [Fact]
+ public void HostFxrPathProperty_SetWhenRunningSDKCommand()
+ {
+ var dotnet = sharedState.MockSDK;
+ dotnet.Exec("--info")
+ .EnableTracingAndCaptureOutputs()
+ .Execute()
+ .Should().Pass()
+ .And.HaveStdErrContaining($"Property {sharedState.HostFxrPathPropertyName} = {dotnet.GreatestVersionHostFxrFilePath}");
+ }
+
+ [Fact]
+ public void HostFxrPathProperty_NotVisibleFromApp()
+ {
+ var fixture = sharedState.RuntimePropertiesFixture
+ .Copy();
+
+ var dotnet = fixture.BuiltDotnet;
+ var appDll = fixture.TestProject.AppDll;
+ dotnet.Exec(appDll, sharedState.HostFxrPathPropertyName)
+ .EnableTracingAndCaptureOutputs()
+ .Execute()
+ .Should().Pass()
+ .And.HaveStdOutContaining($"Property '{sharedState.HostFxrPathPropertyName}' was not found.");
+ }
+
[Fact]
public void DuplicateCommonProperty_Fails()
{
@@ -88,9 +109,7 @@ public void DuplicateCommonProperty_Fails()
var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;
dotnet.Exec(appDll)
- .EnvironmentVariable("COREHOST_TRACE", "1")
- .CaptureStdErr()
- .CaptureStdOut()
+ .EnableTracingAndCaptureOutputs()
.Execute()
.Should().Fail()
.And.HaveStdErrContaining($"Duplicate runtime property found: {name}");
@@ -100,11 +119,13 @@ public class SharedTestState : IDisposable
{
public TestProjectFixture RuntimePropertiesFixture { get; }
public RepoDirectoriesProvider RepoDirectories { get; }
+ public DotNetCli MockSDK { get; }
public string AppTestPropertyName => "APP_TEST_PROPERTY";
public string AppTestPropertyValue => "VALUE_FROM_APP";
public string FrameworkTestPropertyName => "FRAMEWORK_TEST_PROPERTY";
public string FrameworkTestPropertyValue => "VALUE_FROM_FRAMEWORK";
+ public string HostFxrPathPropertyName => "HOSTFXR_PATH";
private readonly string copiedDotnet;
@@ -113,6 +134,19 @@ public SharedTestState()
copiedDotnet = Path.Combine(TestArtifact.TestArtifactsPath, "runtimeProperties");
SharedFramework.CopyDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), copiedDotnet);
+ MockSDK = new DotNetBuilder(copiedDotnet, Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), "exe")
+ .AddMicrosoftNETCoreAppFrameworkMockCoreClr("9999.0.0")
+ .AddMockSDK("9999.0.0-dev", "9999.0.0")
+ .Build();
+
+ File.WriteAllText(Path.Combine(MockSDK.BinPath, "global.json"),
+ @"
+{
+ ""sdk"": {
+ ""version"": ""9999.0.0-dev""
+ }
+}");
+
RepoDirectories = new RepoDirectoriesProvider(builtDotnet: copiedDotnet);
RuntimePropertiesFixture = new TestProjectFixture("RuntimeProperties", RepoDirectories)
diff --git a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs
index 2ee5ffc39c4f7..b1777c1c1e585 100644
--- a/src/libraries/Common/src/Internal/Cryptography/Helpers.cs
+++ b/src/libraries/Common/src/Internal/Cryptography/Helpers.cs
@@ -30,17 +30,12 @@ internal static partial class Helpers
return (byte[])(src.Clone());
}
- public static int GetPaddingSize(this SymmetricAlgorithm algorithm, CipherMode mode, int feedbackSizeBits)
+ public static int GetPaddingSize(this SymmetricAlgorithm algorithm, CipherMode mode, int feedbackSizeInBits)
{
- // CFB8 does not require any padding at all
- // otherwise, it is always required to pad for block size
- if (mode == CipherMode.CFB && feedbackSizeBits == 8)
- return 1;
-
- return algorithm.BlockSize / 8;
+ return (mode == CipherMode.CFB ? feedbackSizeInBits : algorithm.BlockSize) / 8;
}
- internal static bool TryCopyToDestination(ReadOnlySpan source, Span destination, out int bytesWritten)
+ internal static bool TryCopyToDestination(this ReadOnlySpan source, Span destination, out int bytesWritten)
{
if (source.TryCopyTo(destination))
{
diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs
index eeb472a19e5d7..8be9f32cbedee 100644
--- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs
+++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs
@@ -15,8 +15,7 @@ internal static partial class procfs
{
private const string MapsFileName = "/maps";
- private static string GetMapsFilePathForProcess(int pid) =>
- RootPath + pid.ToString(CultureInfo.InvariantCulture) + MapsFileName;
+ private static string GetMapsFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{MapsFileName}");
internal static ProcessModuleCollection? ParseMapsModules(int pid)
{
diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs
index 5dc24477beef5..22b06128926fa 100644
--- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs
+++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs
@@ -30,10 +30,7 @@ internal struct ParsedStatus
internal ulong VmPeak;
}
- internal static string GetStatusFilePathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatusFileName;
- }
+ internal static string GetStatusFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatusFileName}");
internal static bool TryReadStatusFile(int pid, out ParsedStatus result)
{
diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs
index 0ca179b736eca..a85ec0a5f4ffb 100644
--- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs
+++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs
@@ -76,41 +76,17 @@ internal struct ParsedStat
//internal long cguest_time;
}
- internal static string GetExeFilePathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + ExeFileName;
- }
+ internal static string GetExeFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{ExeFileName}");
- internal static string GetCmdLinePathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + CmdLineFileName;
- }
+ internal static string GetCmdLinePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{CmdLineFileName}");
- internal static string GetStatFilePathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatFileName;
- }
+ internal static string GetStatFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatFileName}");
- internal static string GetTaskDirectoryPathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + TaskDirectoryName;
- }
+ internal static string GetTaskDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}");
- internal static string GetFileDescriptorDirectoryPathForProcess(int pid)
- {
- return RootPath + pid.ToString(CultureInfo.InvariantCulture) + FileDescriptorDirectoryName;
- }
+ internal static string GetFileDescriptorDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{FileDescriptorDirectoryName}");
- private static string GetStatFilePathForThread(int pid, int tid)
- {
- // Perf note: Calling GetTaskDirectoryPathForProcess will allocate a string,
- // which we then use in another Concat call to produce another string. The straightforward alternative,
- // though, since we have five input strings, is to use the string.Concat overload that takes a params array.
- // This results in allocating not only the params array but also a defensive copy inside of Concat,
- // which means allocating two five-element arrays. This two-string approach will result not only in fewer
- // allocations, but also typically in less memory allocated, and it's a bit more maintainable.
- return GetTaskDirectoryPathForProcess(pid) + tid.ToString(CultureInfo.InvariantCulture) + StatFileName;
- }
+ private static string GetStatFilePathForThread(int pid, int tid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}{(uint)tid}{StatFileName}");
internal static bool TryReadStatFile(int pid, out ParsedStat result)
{
diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs
index 862760aa2c8de..c93319994bbd5 100644
--- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs
+++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs
@@ -401,7 +401,7 @@ internal sealed class SafeTemporaryKeychainHandle : SafeKeychainHandle
private static readonly Dictionary s_lookup =
new Dictionary();
- internal SafeTemporaryKeychainHandle()
+ public SafeTemporaryKeychainHandle()
{
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs
new file mode 100644
index 0000000000000..2fd31563f1745
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetGroups.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ internal static unsafe uint[]? GetGroups()
+ {
+ const int InitialGroupsLength =
+#if DEBUG
+ 1;
+#else
+ 64;
+#endif
+ Span groups = stackalloc uint[InitialGroupsLength];
+ do
+ {
+ int rv;
+ fixed (uint* pGroups = groups)
+ {
+ rv = Interop.Sys.GetGroups(groups.Length, pGroups);
+ }
+
+ if (rv >= 0)
+ {
+ // success
+ return groups.Slice(0, rv).ToArray();
+ }
+ else if (rv == -1 && Interop.Sys.GetLastError() == Interop.Error.EINVAL)
+ {
+ // increase buffer size
+ groups = new uint[groups.Length * 2];
+ }
+ else
+ {
+ // failure
+ return null;
+ }
+ }
+ while (true);
+ }
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetGroups", SetLastError = true)]
+ private static extern unsafe int GetGroups(int ngroups, uint* groups);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs
index cbc54d4072c42..8248077deaf50 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Permissions.cs
@@ -26,6 +26,8 @@ internal enum Permissions
S_IROTH = 0x4,
S_IWOTH = 0x2,
S_IXOTH = 0x1,
+
+ S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH,
}
}
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs
index b42b5138ec622..65e3ca35f4844 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.PosixSignal.cs
@@ -18,7 +18,7 @@ internal static partial class Sys
internal static extern void DisablePosixSignalHandling(int signal);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_HandleNonCanceledPosixSignal")]
- internal static extern bool HandleNonCanceledPosixSignal(int signal, int handlersDisposed);
+ internal static extern void HandleNonCanceledPosixSignal(int signal);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPlatformSignalNumber")]
[SuppressGCTransition]
diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs
index a2c6255541e28..f334479290011 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs
@@ -19,7 +19,7 @@ internal struct GssBuffer : IDisposable
internal int Copy(byte[] destination, int offset)
{
Debug.Assert(destination != null, "target destination cannot be null");
- Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, "invalid offset " + offset);
+ Debug.Assert((offset >= 0 && offset < destination.Length) || destination.Length == 0, $"invalid offset {offset}");
if (_data == IntPtr.Zero || _length == 0)
{
diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
index c8849d343d9ce..6437c1a7addc4 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
@@ -11,6 +11,24 @@ internal static partial class Interop
{
internal static partial class Crypto
{
+ [DllImport(Libraries.CryptoNative)]
+ private static extern SafeEvpPKeyHandle CryptoNative_EvpPKeyCreateRsa(IntPtr rsa);
+
+ internal static SafeEvpPKeyHandle EvpPKeyCreateRsa(IntPtr rsa)
+ {
+ Debug.Assert(rsa != IntPtr.Zero);
+
+ SafeEvpPKeyHandle pkey = CryptoNative_EvpPKeyCreateRsa(rsa);
+
+ if (pkey.IsInvalid)
+ {
+ pkey.Dispose();
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return pkey;
+ }
+
[DllImport(Libraries.CryptoNative)]
private static extern SafeEvpPKeyHandle CryptoNative_RsaGenerateKey(int keySize);
@@ -171,16 +189,5 @@ ref MemoryMarshal.GetReference(signature),
Debug.Assert(ret == -1);
throw CreateOpenSslCryptographicException();
}
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
- internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeySetRsa")]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool EvpPkeySetRsa(SafeEvpPKeyHandle pkey, SafeRsaHandle rsa);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeySetRsa")]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool EvpPkeySetRsa(SafeEvpPKeyHandle pkey, IntPtr rsa);
}
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
index 8a3fc840b1610..924c2da14d4b5 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
@@ -12,6 +13,30 @@ internal static partial class Crypto
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyCreate")]
internal static extern SafeEvpPKeyHandle EvpPkeyCreate();
+ [DllImport(Libraries.CryptoNative)]
+ private static extern SafeEvpPKeyHandle CryptoNative_EvpPKeyDuplicate(
+ SafeEvpPKeyHandle currentKey,
+ EvpAlgorithmId algorithmId);
+
+ internal static SafeEvpPKeyHandle EvpPKeyDuplicate(
+ SafeEvpPKeyHandle currentKey,
+ EvpAlgorithmId algorithmId)
+ {
+ Debug.Assert(!currentKey.IsInvalid);
+
+ SafeEvpPKeyHandle pkey = CryptoNative_EvpPKeyDuplicate(
+ currentKey,
+ algorithmId);
+
+ if (pkey.IsInvalid)
+ {
+ pkey.Dispose();
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return pkey;
+ }
+
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyDestroy")]
internal static extern void EvpPkeyDestroy(IntPtr pkey);
@@ -20,5 +45,173 @@ internal static partial class Crypto
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_UpRefEvpPkey")]
internal static extern int UpRefEvpPkey(SafeEvpPKeyHandle handle);
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern unsafe SafeEvpPKeyHandle CryptoNative_DecodeSubjectPublicKeyInfo(
+ byte* buf,
+ int len,
+ int algId);
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern unsafe SafeEvpPKeyHandle CryptoNative_DecodePkcs8PrivateKey(
+ byte* buf,
+ int len,
+ int algId);
+
+ internal static unsafe SafeEvpPKeyHandle DecodeSubjectPublicKeyInfo(
+ ReadOnlySpan source,
+ EvpAlgorithmId algorithmId)
+ {
+ SafeEvpPKeyHandle handle;
+
+ fixed (byte* sourcePtr = source)
+ {
+ handle = CryptoNative_DecodeSubjectPublicKeyInfo(
+ sourcePtr,
+ source.Length,
+ (int)algorithmId);
+ }
+
+ if (handle.IsInvalid)
+ {
+ handle.Dispose();
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return handle;
+ }
+
+ internal static unsafe SafeEvpPKeyHandle DecodePkcs8PrivateKey(
+ ReadOnlySpan source,
+ EvpAlgorithmId algorithmId)
+ {
+ SafeEvpPKeyHandle handle;
+
+ fixed (byte* sourcePtr = source)
+ {
+ handle = CryptoNative_DecodePkcs8PrivateKey(
+ sourcePtr,
+ source.Length,
+ (int)algorithmId);
+ }
+
+ if (handle.IsInvalid)
+ {
+ handle.Dispose();
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return handle;
+ }
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern int CryptoNative_GetPkcs8PrivateKeySize(IntPtr pkey);
+
+ private static int GetPkcs8PrivateKeySize(IntPtr pkey)
+ {
+ int ret = CryptoNative_GetPkcs8PrivateKeySize(pkey);
+
+ if (ret < 0)
+ {
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return ret;
+ }
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern unsafe int CryptoNative_EncodePkcs8PrivateKey(IntPtr pkey, byte* buf);
+
+ internal static ArraySegment RentEncodePkcs8PrivateKey(SafeEvpPKeyHandle pkey)
+ {
+ bool addedRef = false;
+
+ try
+ {
+ pkey.DangerousAddRef(ref addedRef);
+ IntPtr handle = pkey.DangerousGetHandle();
+
+ int size = GetPkcs8PrivateKeySize(handle);
+ byte[] rented = CryptoPool.Rent(size);
+ int written;
+
+ unsafe
+ {
+ fixed (byte* buf = rented)
+ {
+ written = CryptoNative_EncodePkcs8PrivateKey(handle, buf);
+ }
+ }
+
+ Debug.Assert(written == size);
+ return new ArraySegment(rented, 0, written);
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ pkey.DangerousRelease();
+ }
+ }
+ }
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern int CryptoNative_GetSubjectPublicKeyInfoSize(IntPtr pkey);
+
+ private static int GetSubjectPublicKeyInfoSize(IntPtr pkey)
+ {
+ int ret = CryptoNative_GetSubjectPublicKeyInfoSize(pkey);
+
+ if (ret < 0)
+ {
+ throw CreateOpenSslCryptographicException();
+ }
+
+ return ret;
+ }
+
+ [DllImport(Libraries.CryptoNative)]
+ private static extern unsafe int CryptoNative_EncodeSubjectPublicKeyInfo(IntPtr pkey, byte* buf);
+
+ internal static ArraySegment RentEncodeSubjectPublicKeyInfo(SafeEvpPKeyHandle pkey)
+ {
+ bool addedRef = false;
+
+ try
+ {
+ pkey.DangerousAddRef(ref addedRef);
+ IntPtr handle = pkey.DangerousGetHandle();
+
+ int size = GetSubjectPublicKeyInfoSize(handle);
+ byte[] rented = CryptoPool.Rent(size);
+ int written;
+
+ unsafe
+ {
+ fixed (byte* buf = rented)
+ {
+ written = CryptoNative_EncodeSubjectPublicKeyInfo(handle, buf);
+ }
+ }
+
+ Debug.Assert(written == size);
+ return new ArraySegment(rented, 0, written);
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ pkey.DangerousRelease();
+ }
+ }
+ }
+
+ internal enum EvpAlgorithmId
+ {
+ Unknown = 0,
+ RSA = 6,
+ DSA = 116,
+ ECC = 408,
+ }
}
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
index 604ac8b4c7980..9dd310326b0ad 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
@@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Net;
using System.Net.Security;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -223,6 +224,19 @@ internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX50
return context;
}
+ internal static SecurityStatusPal SslRenegotiate(SafeSslHandle sslContext, out byte[]? outputBuffer)
+ {
+ int ret = Interop.Ssl.SslRenegotiate(sslContext);
+
+ outputBuffer = Array.Empty();
+ if (ret != 1)
+ {
+ GetSslError(sslContext, ret, out Exception? exception);
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exception);
+ }
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ }
+
internal static bool DoSslHandshake(SafeSslHandle context, ReadOnlySpan input, out byte[]? sendBuf, out int sendCount)
{
sendBuf = null;
@@ -295,7 +309,7 @@ internal static int Encrypt(SafeSslHandle context, ReadOnlySpan input, ref
{
#if DEBUG
ulong assertNoError = Crypto.ErrPeekError();
- Debug.Assert(assertNoError == 0, "OpenSsl error queue is not empty, run: 'openssl errstr " + assertNoError.ToString("X") + "' for original error.");
+ Debug.Assert(assertNoError == 0, $"OpenSsl error queue is not empty, run: 'openssl errstr {assertNoError:X}' for original error.");
#endif
errorCode = Ssl.SslErrorCode.SSL_ERROR_NONE;
@@ -349,7 +363,7 @@ internal static int Decrypt(SafeSslHandle context, Span buffer, out Ssl.Ss
{
#if DEBUG
ulong assertNoError = Crypto.ErrPeekError();
- Debug.Assert(assertNoError == 0, "OpenSsl error queue is not empty, run: 'openssl errstr " + assertNoError.ToString("X") + "' for original error.");
+ Debug.Assert(assertNoError == 0, $"OpenSsl error queue is not empty, run: 'openssl errstr {assertNoError:X}' for original error.");
#endif
errorCode = Ssl.SslErrorCode.SSL_ERROR_NONE;
diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
deleted file mode 100644
index 105258194845c..0000000000000
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security.Cryptography;
-using Microsoft.Win32.SafeHandles;
-
-internal static partial class Interop
-{
- internal static partial class Crypto
- {
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaCreate")]
- internal static extern SafeRsaHandle RsaCreate();
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaUpRef")]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool RsaUpRef(IntPtr rsa);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaDestroy")]
- internal static extern void RsaDestroy(IntPtr rsa);
-
- internal static SafeRsaHandle DecodeRsaPublicKey(ReadOnlySpan buf) =>
- DecodeRsaPublicKey(ref MemoryMarshal.GetReference(buf), buf.Length);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DecodeRsaPublicKey")]
- private static extern SafeRsaHandle DecodeRsaPublicKey(ref byte buf, int len);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSize")]
- internal static extern int RsaSize(SafeRsaHandle rsa);
-
- internal static RSAParameters ExportRsaParameters(SafeEvpPKeyHandle key, bool includePrivateParameters)
- {
- using (SafeRsaHandle rsa = EvpPkeyGetRsa(key))
- {
- return ExportRsaParameters(rsa, includePrivateParameters);
- }
- }
-
- internal static RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includePrivateParameters)
- {
- Debug.Assert(
- key != null && !key.IsInvalid,
- "Callers should check the key is invalid and throw an exception with a message");
-
- if (key == null || key.IsInvalid)
- {
- throw new CryptographicException();
- }
-
- bool addedRef = false;
-
- try
- {
- key.DangerousAddRef(ref addedRef);
-
- IntPtr n, e, d, p, dmp1, q, dmq1, iqmp;
- if (!GetRsaParameters(key, out n, out e, out d, out p, out dmp1, out q, out dmq1, out iqmp))
- {
- throw new CryptographicException();
- }
-
- int modulusSize = Crypto.RsaSize(key);
-
- // RSACryptoServiceProvider expects P, DP, Q, DQ, and InverseQ to all
- // be padded up to half the modulus size.
- int halfModulus = modulusSize / 2;
-
- RSAParameters rsaParameters = new RSAParameters
- {
- Modulus = Crypto.ExtractBignum(n, modulusSize)!,
- Exponent = Crypto.ExtractBignum(e, 0)!,
- };
-
- if (includePrivateParameters)
- {
- rsaParameters.D = Crypto.ExtractBignum(d, modulusSize);
- rsaParameters.P = Crypto.ExtractBignum(p, halfModulus);
- rsaParameters.DP = Crypto.ExtractBignum(dmp1, halfModulus);
- rsaParameters.Q = Crypto.ExtractBignum(q, halfModulus);
- rsaParameters.DQ = Crypto.ExtractBignum(dmq1, halfModulus);
- rsaParameters.InverseQ = Crypto.ExtractBignum(iqmp, halfModulus);
- }
-
- return rsaParameters;
- }
- finally
- {
- if (addedRef)
- key.DangerousRelease();
- }
- }
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetRsaParameters")]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool GetRsaParameters(
- SafeRsaHandle key,
- out IntPtr n,
- out IntPtr e,
- out IntPtr d,
- out IntPtr p,
- out IntPtr dmp1,
- out IntPtr q,
- out IntPtr dmq1,
- out IntPtr iqmp);
-
- [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SetRsaParameters")]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool SetRsaParameters(
- SafeRsaHandle key,
- byte[]? n,
- int nLength,
- byte[]? e,
- int eLength,
- byte[]? d,
- int dLength,
- byte[]? p,
- int pLength,
- byte[]? dmp1,
- int dmp1Length,
- byte[]? q,
- int qLength,
- byte[]? dmq1,
- int dmq1Length,
- byte[]? iqmp,
- int iqmpLength);
-
- internal enum RsaPadding : int
- {
- Pkcs1 = 0,
- OaepSHA1 = 1,
- NoPadding = 2,
- }
- }
-}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
index d080cf2f0d7b4..29154b77da632 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
@@ -74,6 +74,9 @@ internal static partial class Ssl
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRead", SetLastError = true)]
internal static extern int SslRead(SafeSslHandle ssl, ref byte buf, int num);
+ [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRenegotiate")]
+ internal static extern int SslRenegotiate(SafeSslHandle ssl);
+
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_IsSslRenegotiatePending")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool IsSslRenegotiatePending(SafeSslHandle ssl);
diff --git a/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs b/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs
index e0982d907c687..27c22800a42d5 100644
--- a/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs
+++ b/src/libraries/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs
@@ -508,7 +508,7 @@ internal sealed class SafeLocalFreeChannelBinding : ChannelBinding
{
private int _size;
- private SafeLocalFreeChannelBinding() { }
+ public SafeLocalFreeChannelBinding() { }
public override int Size
{
diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs
index 4326327034382..c73b065ea5953 100644
--- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs
+++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetObjectContext.cs
@@ -9,7 +9,7 @@ internal static partial class Interop
{
internal static partial class Ole32
{
- [DllImport(Libraries.Ole32, PreserveSig = false)]
- internal static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease);
+ [DllImport(Libraries.Ole32)]
+ internal static extern int CoGetObjectContext([MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs
index 1b153d786dfba..d65814a2f51c6 100644
--- a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs
+++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CreateStreamOnHGlobal.cs
@@ -8,7 +8,7 @@ internal static partial class Interop
{
internal static partial class Ole32
{
- [DllImport(Libraries.Ole32)]
- internal static extern int CoGetObjectContext([MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
+ [DllImport(Libraries.Ole32, PreserveSig = false)]
+ internal static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease);
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs
index b675f7f022473..1694ca539a86a 100644
--- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs
+++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs
@@ -65,6 +65,7 @@ internal enum ContextAttribute
SECPKG_ATTR_LOCAL_CERT_CONTEXT = 0x54, // returns PCCERT_CONTEXT
SECPKG_ATTR_ROOT_STORE = 0x55, // returns HCERTCONTEXT to the root store
SECPKG_ATTR_ISSUER_LIST_EX = 0x59, // returns SecPkgContext_IssuerListInfoEx
+ SECPKG_ATTR_CLIENT_CERT_POLICY = 0x60, // sets SecPkgCred_ClientCertCtlPolicy
SECPKG_ATTR_CONNECTION_INFO = 0x5A, // returns SecPkgContext_ConnectionInfo
SECPKG_ATTR_CIPHER_INFO = 0x64, // returns SecPkgContext_CipherInfo
SECPKG_ATTR_UI_INFO = 0x68, // sets SEcPkgContext_UiInfo
@@ -315,6 +316,20 @@ public SecBufferDesc(int count)
}
}
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct SecPkgCred_ClientCertPolicy
+ {
+ public uint dwFlags;
+ public Guid guidPolicyId;
+ public uint dwCertFlags;
+ public uint dwUrlRetrievalTimeout;
+ public BOOL fCheckRevocationFreshnessTime;
+ public uint dwRevocationFreshnessTime;
+ public BOOL fOmitUsageCheck;
+ public char* pwszSslCtlStoreName;
+ public char* pwszSslCtlIdentifier;
+ }
+
[DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, SetLastError = true)]
internal static extern int EncryptMessage(
ref CredHandle contextHandle,
@@ -472,5 +487,12 @@ internal static extern SECURITY_STATUS SspiEncodeStringsAsAuthIdentity(
[In] string domainName,
[In] string password,
[Out] out SafeSspiAuthDataHandle authData);
+
+ [DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern SECURITY_STATUS SetCredentialsAttributesW(
+ [In] ref CredHandle handlePtr,
+ [In] long ulAttribute,
+ [In] ref SecPkgCred_ClientCertPolicy pBuffer,
+ [In] long cbBuffer);
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs
index ea65be9600eba..ada41ba189fc5 100644
--- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs
+++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs
@@ -59,14 +59,7 @@ internal unsafe SecurityPackageInfoClass(SafeHandle safeHandle, int index)
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, this.ToString());
}
- public override string ToString()
- {
- return "Capabilities:" + string.Format(CultureInfo.InvariantCulture, "0x{0:x}", Capabilities)
- + " Version:" + Version.ToString(NumberFormatInfo.InvariantInfo)
- + " RPCID:" + RPCID.ToString(NumberFormatInfo.InvariantInfo)
- + " MaxToken:" + MaxToken.ToString(NumberFormatInfo.InvariantInfo)
- + " Name:" + ((Name == null) ? "(null)" : Name)
- + " Comment:" + ((Comment == null) ? "(null)" : Comment);
- }
+ public override string ToString() =>
+ $"Capabilities:0x{Capabilities:x} Version:{Version} RPCID:{RPCID} MaxToken:{MaxToken} Name:{Name ?? "(null)"} Comment: {Comment ?? "(null)"}";
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs
index 0f2cb05603aca..43b718524d1ca 100644
--- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs
+++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs
@@ -128,7 +128,7 @@ public static int SetContextAttributes(
internal sealed class SafeFreeContextBuffer_SECURITY : SafeFreeContextBuffer
{
- internal SafeFreeContextBuffer_SECURITY() : base() { }
+ public SafeFreeContextBuffer_SECURITY() : base() { }
protected override bool ReleaseHandle()
{
diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs
index 7ac85b3155bbc..82a8218338ca5 100644
--- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs
+++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs
@@ -13,8 +13,8 @@ internal static partial class WinHttp
public static extern SafeWinHttpHandle WinHttpOpen(
IntPtr userAgent,
uint accessType,
- string proxyName,
- string proxyBypass, int flags);
+ string? proxyName,
+ string? proxyBypass, int flags);
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -33,7 +33,7 @@ public static extern SafeWinHttpHandle WinHttpOpenRequest(
SafeWinHttpHandle connectHandle,
string verb,
string objectName,
- string version,
+ string? version,
string referrer,
string acceptTypes,
uint flags);
@@ -161,8 +161,8 @@ public static extern bool WinHttpSetCredentials(
SafeWinHttpHandle requestHandle,
uint authTargets,
uint authScheme,
- string userName,
- string password,
+ string? userName,
+ string? password,
IntPtr reserved);
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs
index 193533551bcea..f8b26951cf226 100644
--- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs
+++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs
@@ -68,7 +68,7 @@ public override bool IsInvalid
internal sealed class SafeSharedAsn1IntegerHandle : SafeInteriorHandle
{
- private SafeSharedAsn1IntegerHandle() :
+ public SafeSharedAsn1IntegerHandle() :
base(IntPtr.Zero, ownsHandle: true)
{
}
@@ -76,7 +76,7 @@ private SafeSharedAsn1IntegerHandle() :
internal sealed class SafeSharedAsn1OctetStringHandle : SafeInteriorHandle
{
- private SafeSharedAsn1OctetStringHandle() :
+ public SafeSharedAsn1OctetStringHandle() :
base(IntPtr.Zero, ownsHandle: true)
{
}
diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs
index 0c6a8304620ad..20eeb6dfbf818 100644
--- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs
+++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/GssSafeHandles.PlatformNotSupported.cs
@@ -18,7 +18,8 @@ public override bool IsInvalid
}
protected override bool ReleaseHandle() => throw new PlatformNotSupportedException();
- private SafeGssNameHandle()
+
+ public SafeGssNameHandle()
: base(IntPtr.Zero, true)
{
}
@@ -27,7 +28,7 @@ private SafeGssNameHandle()
[UnsupportedOSPlatform("tvos")]
internal sealed class SafeGssCredHandle : SafeHandle
{
- private SafeGssCredHandle()
+ public SafeGssCredHandle()
: base(IntPtr.Zero, true)
{
}
@@ -43,7 +44,7 @@ public override bool IsInvalid
[UnsupportedOSPlatform("tvos")]
internal sealed class SafeGssContextHandle : SafeHandle
{
- private SafeGssContextHandle()
+ public SafeGssContextHandle()
: base(IntPtr.Zero, true)
{
}
diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs
deleted file mode 100644
index aef516adb3a30..0000000000000
--- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Security;
-using System.Runtime.InteropServices;
-
-namespace Microsoft.Win32.SafeHandles
-{
- internal sealed class SafeRsaHandle : SafeHandle
- {
- public SafeRsaHandle() :
- base(IntPtr.Zero, ownsHandle: true)
- {
- }
-
- protected override bool ReleaseHandle()
- {
- Interop.Crypto.RsaDestroy(handle);
- SetHandle(IntPtr.Zero);
- return true;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- internal static SafeRsaHandle DuplicateHandle(IntPtr handle)
- {
- Debug.Assert(handle != IntPtr.Zero);
-
- // Reliability: Allocate the SafeHandle before calling RSA_up_ref so
- // that we don't lose a tracked reference in low-memory situations.
- SafeRsaHandle safeHandle = new SafeRsaHandle();
-
- if (!Interop.Crypto.RsaUpRef(handle))
- {
- throw Interop.Crypto.CreateOpenSslCryptographicException();
- }
-
- safeHandle.SetHandle(handle);
- return safeHandle;
- }
- }
-}
diff --git a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs
index e58bfd19695ea..0c27a20049577 100644
--- a/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs
+++ b/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeUnicodeStringHandle.cs
@@ -4,6 +4,8 @@
using System;
using System.Runtime.InteropServices;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace Microsoft.Win32.SafeHandles
{
///
diff --git a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs
index 281691f7907f6..4ddf9950f5cc7 100644
--- a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs
+++ b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs
@@ -327,7 +327,7 @@ public string BaseType
string returnType = _baseType;
return _needsFixup && TypeArguments.Count > 0 ?
- returnType + '`' + TypeArguments.Count.ToString(CultureInfo.InvariantCulture) :
+ $"{returnType}`{(uint)TypeArguments.Count}" :
returnType;
}
set
diff --git a/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs b/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs
index 8630d366a866e..f716210055d63 100644
--- a/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs
+++ b/src/libraries/Common/src/System/Composition/Diagnostics/DebuggerTraceWriter.cs
@@ -57,8 +57,7 @@ private static string CreateLogMessage(TraceEventType eventType, CompositionTrac
StringBuilder messageBuilder = new StringBuilder();
// Format taken from TraceListener.TraceEvent in .NET Framework
- messageBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}: {2} : ",
- s_sourceName, eventType.ToString(), (int)traceId);
+ messageBuilder.Append($"{s_sourceName} {eventType}: {(int)traceId} : ");
if (arguments == null)
{
diff --git a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs
index 80a092104ae50..54df9fa779826 100644
--- a/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs
+++ b/src/libraries/Common/src/System/Data/Common/DbConnectionOptions.Common.cs
@@ -525,11 +525,11 @@ private static void ParseComparison(Dictionary parsetable, stri
}
}
}
- Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">");
+ Debug.Assert(isEquivalent, $"ParseInternal code vs regex message mismatch: <{msg1}> <{msg2}>");
}
else
{
- Debug.Fail("ParseInternal code vs regex throw mismatch " + f.Message);
+ Debug.Fail($"ParseInternal code vs regex throw mismatch {f.Message}");
}
e = null;
}
diff --git a/src/libraries/Common/src/System/Drawing/ColorTranslator.cs b/src/libraries/Common/src/System/Drawing/ColorTranslator.cs
index d1caed09be28d..b8fc7866abe24 100644
--- a/src/libraries/Common/src/System/Drawing/ColorTranslator.cs
+++ b/src/libraries/Common/src/System/Drawing/ColorTranslator.cs
@@ -386,9 +386,7 @@ public static string ToHtml(Color c)
}
else
{
- colorString = "#" + c.R.ToString("X2", null) +
- c.G.ToString("X2", null) +
- c.B.ToString("X2", null);
+ colorString = $"#{c.R:X2}{c.G:X2}{c.B:X2}";
}
return colorString;
diff --git a/src/libraries/Common/src/System/IO/RowConfigReader.cs b/src/libraries/Common/src/System/IO/RowConfigReader.cs
index a6bfd96daf413..571aa9afd9e8a 100644
--- a/src/libraries/Common/src/System/IO/RowConfigReader.cs
+++ b/src/libraries/Common/src/System/IO/RowConfigReader.cs
@@ -132,7 +132,7 @@ private bool TryFindNextKeyOccurrence(string key, int startIndex, out int keyInd
}
// Check If the match is at the beginning of the string, or is preceded by a newline.
else if (keyIndex == 0
- || (keyIndex >= Environment.NewLine.Length && _buffer.Substring(keyIndex - Environment.NewLine.Length, Environment.NewLine.Length) == Environment.NewLine))
+ || (keyIndex >= Environment.NewLine.Length && _buffer.AsSpan(keyIndex - Environment.NewLine.Length, Environment.NewLine.Length).SequenceEqual(Environment.NewLine)))
{
// Check if the match is followed by whitespace, meaning it is not part of a larger word.
if (HasFollowingWhitespace(keyIndex, key.Length))
diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs
index 4e4e0c672b496..a145951254a76 100644
--- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs
+++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs
@@ -212,7 +212,7 @@ internal static int VerifySignature(SafeDeleteContext securityContext, byte[] bu
// throw if error
if (errorCode != 0)
{
- NetEventSource.Info($"VerifySignature threw error: {errorCode.ToString("x", NumberFormatInfo.InvariantInfo)}");
+ NetEventSource.Info($"VerifySignature threw error: {errorCode:x}");
throw new Win32Exception(errorCode);
}
@@ -256,7 +256,7 @@ internal static int MakeSignature(SafeDeleteContext securityContext, byte[] buff
// throw if error
if (errorCode != 0)
{
- NetEventSource.Info($"MakeSignature threw error: {errorCode.ToString("x", NumberFormatInfo.InvariantInfo)}");
+ NetEventSource.Info($"MakeSignature threw error: {errorCode:x}");
throw new Win32Exception(errorCode);
}
diff --git a/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs b/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs
index 8e889f852afc3..4f4c9951a72ed 100644
--- a/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs
+++ b/src/libraries/Common/src/System/Net/Security/SecurityContextTokenHandle.cs
@@ -16,7 +16,7 @@ internal sealed class SecurityContextTokenHandle : CriticalHandleZeroOrMinusOneI
#endif
private int _disposed;
- private SecurityContextTokenHandle() : base()
+ public SecurityContextTokenHandle() : base()
{
}
diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs
index 123a28ace0f49..2e5a0b8560027 100644
--- a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs
+++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs
@@ -7,6 +7,8 @@
using System.Text;
using Microsoft.Win32.SafeHandles;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace System.Net.Security
{
internal sealed class SafeDeleteNegoContext : SafeDeleteContext
diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs
index 9b27cc0d969fd..3f550d36d7f43 100644
--- a/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs
+++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs
@@ -11,6 +11,8 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace System.Net.Security
{
internal sealed class SafeDeleteSslContext : SafeDeleteContext
diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs
index c5fa820ab9279..ca7438a33b483 100644
--- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs
+++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeCertContext.cs
@@ -8,6 +8,8 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace System.Net.Security
{
#if DEBUG
diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs
index 58d1942829e52..fef571d5f7dbd 100644
--- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs
+++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs
@@ -7,6 +7,8 @@
using System.Text;
using Microsoft.Win32.SafeHandles;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace System.Net.Security
{
internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials
diff --git a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs
index 8867027d07042..ff378ab425139 100644
--- a/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs
+++ b/src/libraries/Common/src/System/Net/Security/Unix/SafeFreeSslCredentials.cs
@@ -10,6 +10,8 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+#pragma warning disable CA1419 // TODO https://github.com/dotnet/roslyn-analyzers/issues/5232: not intended for use with P/Invoke
+
namespace System.Net.Security
{
internal sealed class SafeFreeSslCredentials : SafeFreeCredentials
diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
index d06ce8117fc5a..8de1e2a0f4ce3 100644
--- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
@@ -7,12 +7,8 @@
namespace System.Net
{
- internal static class SocketProtocolSupportPal
+ internal static partial class SocketProtocolSupportPal
{
- public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6);
- public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
- public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
-
private static unsafe bool IsSupported(AddressFamily af)
{
IntPtr invalid = (IntPtr)(-1);
diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
index de0465a51c6c7..50e7db176e2f1 100644
--- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
@@ -9,12 +9,8 @@
namespace System.Net
{
- internal static class SocketProtocolSupportPal
+ internal static partial class SocketProtocolSupportPal
{
- public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6);
- public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
- public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
-
private static bool IsSupported(AddressFamily af)
{
Interop.Winsock.EnsureInitialized();
diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs
new file mode 100644
index 0000000000000..a61f47a0fa458
--- /dev/null
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Sockets;
+
+namespace System.Net
+{
+ internal static partial class SocketProtocolSupportPal
+ {
+ private const string DisableIPv6AppCtxSwitch = "System.Net.DisableIPv6";
+ private const string DisableIPv6EnvironmentVariable = "DOTNET_SYSTEM_NET_DISABLEIPV6";
+
+ public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6) && !IsIPv6Disabled();
+ public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
+ public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
+
+ private static bool IsIPv6Disabled()
+ {
+ // First check for the AppContext switch, giving it priority over the environment variable.
+ if (AppContext.TryGetSwitch(DisableIPv6AppCtxSwitch, out bool disabled))
+ {
+ return disabled;
+ }
+
+ // AppContext switch wasn't used. Check the environment variable.
+ string? envVar = Environment.GetEnvironmentVariable(DisableIPv6EnvironmentVariable);
+
+ if (envVar is not null)
+ {
+ return envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/libraries/Common/src/System/Obsoletions.cs b/src/libraries/Common/src/System/Obsoletions.cs
index 613d51a5ba783..5548be60e6934 100644
--- a/src/libraries/Common/src/System/Obsoletions.cs
+++ b/src/libraries/Common/src/System/Obsoletions.cs
@@ -101,5 +101,8 @@ internal static class Obsoletions
internal const string UseManagedSha1Message = "HMACSHA1 always uses the algorithm implementation provided by the platform. Use a constructor without the useManagedSha1 parameter.";
internal const string UseManagedSha1DiagId = "SYSLIB0030";
+
+ internal const string CryptoConfigEncodeOIDMessage = "EncodeOID is obsolete. Use the ASN.1 functionality provided in System.Formats.Asn1.";
+ internal const string CryptoConfigEncodeOIDDiagId = "SYSLIB0031";
}
}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs
index c7a4c5784ab91..058093035af1e 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs
@@ -32,7 +32,7 @@ internal bool Equals(ref AlgorithmIdentifierAsn other)
return Parameters!.Value.Span.SequenceEqual(other.Parameters!.Value.Span);
}
- internal bool HasNullEquivalentParameters()
+ internal readonly bool HasNullEquivalentParameters()
{
return RepresentsNull(Parameters);
}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs
index 6e22b91fb3687..a16f66c40c89e 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs
@@ -227,5 +227,12 @@ internal static void WriteObjectIdentifierForCrypto(
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
}
+
+ internal static ArraySegment RentAndEncode(this AsnWriter writer)
+ {
+ byte[] rented = CryptoPool.Rent(writer.GetEncodedLength());
+ int written = writer.Encode(rented);
+ return new ArraySegment(rented, 0, written);
+ }
}
}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs
new file mode 100644
index 0000000000000..b4d07d7366aff
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs
@@ -0,0 +1,393 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Formats.Asn1;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography
+{
+ internal static partial class KeyFormatHelper
+ {
+ internal static unsafe void ReadEncryptedPkcs8(
+ string[] validOids,
+ ReadOnlySpan source,
+ ReadOnlySpan password,
+ KeyReader keyReader,
+ out int bytesRead,
+ out TRet ret)
+ {
+ fixed (byte* ptr = &MemoryMarshal.GetReference(source))
+ {
+ using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length))
+ {
+ ReadEncryptedPkcs8(validOids, manager.Memory, password, keyReader, out bytesRead, out ret);
+ }
+ }
+ }
+
+ internal static unsafe void ReadEncryptedPkcs8(
+ string[] validOids,
+ ReadOnlySpan source,
+ ReadOnlySpan passwordBytes,
+ KeyReader keyReader,
+ out int bytesRead,
+ out TRet ret)
+ {
+ fixed (byte* ptr = &MemoryMarshal.GetReference(source))
+ {
+ using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length))
+ {
+ ReadEncryptedPkcs8(
+ validOids,
+ manager.Memory,
+ passwordBytes,
+ keyReader,
+ out bytesRead,
+ out ret);
+ }
+ }
+ }
+
+ private static void ReadEncryptedPkcs8(
+ string[] validOids,
+ ReadOnlyMemory source,
+ ReadOnlySpan