diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 9c120588ffdef..8dd812c7094b4 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
- "version": "1.0.0-prerelease.21330.2",
+ "version": "1.0.0-prerelease.21357.4",
"commands": [
"xharness"
]
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/workflow/testing/using-your-build.md b/docs/workflow/testing/using-your-build.md
index 682a5dcb5234e..a6024d4269bd7 100644
--- a/docs/workflow/testing/using-your-build.md
+++ b/docs/workflow/testing/using-your-build.md
@@ -16,9 +16,9 @@ assume use of a dogfood build of the .NET SDK.
## Acquire the latest nightly .NET SDK
-- [Win 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-win-x64.zip)
-- [macOS 64-bit Latest](https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-osx-x64.tar.gz)
-- [Others](https://github.com/dotnet/cli/blob/master/README.md#installers-and-binaries)
+- [Win 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip)
+- [macOS 64-bit Latest](https://aka.ms/dotnet/6.0/daily/dotnet-sdk-osx-x64.tar.gz)
+- [Others](https://github.com/dotnet/installer#installers-and-binaries)
To setup the SDK download the zip and extract it somewhere and add the root folder to your [path](../requirements/windows-requirements.md#adding-to-the-default-path-variable)
or always fully qualify the path to dotnet in the root of this folder for all the instructions in this document.
@@ -73,8 +73,7 @@ dotnet publish
-
-
+
```
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 410d96a54a378..2389066e4b33e 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -8,223 +8,223 @@
https://github.com/dotnet/msquic
d7db669b70f4dd67ec001c192f9809c218cab88b
-
+
https://github.com/dotnet/emsdk
- f5349765b7af1970c5b25cce4ed278544907cbe0
+ 5c9145289bd4d4e14b18a544dda60a185f66f688
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- ff3e7d23139c30feefe36d3d4e8d41a06160f254
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://github.com/dotnet/arcade
- ff3e7d23139c30feefe36d3d4e8d41a06160f254
+ 286d98094b830b8dad769542b2669cb1b75f7097
https://github.com/microsoft/vstest
140434f7109d357d0158ade9e5164a4861513965
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
-
+
https://github.com/dotnet/llvm-project
- 51c322893cff67a67e503d00e9c328d9d40b6a06
+ a05e5e9fb80f9bb6fd9100775dfe55be6f84729d
https://github.com/dotnet/runtime
38017c3935de95d0335bac04f4901ddfc2718656
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/dotnet/runtime
- f891033db5b8ebf651176a3dcc3bec74a217f85e
+ 5c340e9ade0baf7f3c0aa0a9128bf36b158fe7d6
-
+
https://github.com/mono/linker
- f574448d16af45f7ac2c4b89d71dea73dec86726
+ 35a1c74d6a0dbd115bf079dc986cea59cdb01430
-
+
https://github.com/dotnet/xharness
- 6d17e5ba4709de02f2e5c62a308f8518253cb002
+ c6d444eaf7e95339589ceef371cbef0a90a4add5
-
+
https://github.com/dotnet/xharness
- 6d17e5ba4709de02f2e5c62a308f8518253cb002
+ c6d444eaf7e95339589ceef371cbef0a90a4add5
-
+
https://github.com/dotnet/arcade
- 26345756f99087811b1fe4d02ff213eb172ec506
+ 286d98094b830b8dad769542b2669cb1b75f7097
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f
+ a89f052e97fec59a2d0148c08d3b4801567ec200
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f
+ a89f052e97fec59a2d0148c08d3b4801567ec200
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f
+ a89f052e97fec59a2d0148c08d3b4801567ec200
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 366fc54b3a9b6226dbbbf3672fae78ba82e82b6f
+ a89f052e97fec59a2d0148c08d3b4801567ec200
-
+
https://github.com/dotnet/hotreload-utils
- a8e0dc88077495e43d7820f631815fa95ce92f8a
+ 3960ef9a8980181e840b5c1d64ed0b234711e850
-
+
https://github.com/dotnet/runtime-assets
- 8d7b898b96cbdb868cac343e938173105287ed9e
+ c6b17f3f85cb4ff078f7cd5264a9005f9b8c3334
-
+
https://github.com/dotnet/roslyn-analyzers
- fcddb771f42866f9521f23f093b1f30e129018bb
+ 77c6f0725c26442023c8eee2b143b899cb3f4eb7
diff --git a/eng/Versions.props b/eng/Versions.props
index ba4255d77e5c3..7be0ee173f2f3 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -50,30 +50,30 @@
3.10.0
3.10.0
- 6.0.0-rc1.21324.1
+ 6.0.0-rc1.21356.1
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 2.5.1-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
- 6.0.0-beta.21330.2
+ 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-preview.1.102
6.0.0-alpha.1.20612.4
- 6.0.0-preview.7.21321.2
- 6.0.0-preview.7.21321.2
+ 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21355.1
3.1.0
- 6.0.0-preview.7.21321.2
+ 6.0.0-preview.7.21355.1
5.0.0
4.3.0
@@ -107,27 +107,27 @@
5.0.0
5.0.0
4.8.1
- 6.0.0-preview.7.21321.2
- 6.0.0-preview.7.21321.2
+ 6.0.0-preview.7.21355.1
+ 6.0.0-preview.7.21355.1
4.5.4
4.5.0
- 6.0.0-preview.7.21321.2
+ 6.0.0-preview.7.21355.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21307.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.1
- 6.0.0-beta.21314.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.21356.1
- 1.0.0-prerelease.21330.5
- 1.0.0-prerelease.21330.5
- 1.0.0-prerelease.21330.5
- 1.0.0-prerelease.21330.5
+ 1.0.0-prerelease.21357.3
+ 1.0.0-prerelease.21357.3
+ 1.0.0-prerelease.21357.3
+ 1.0.0-prerelease.21357.3
16.9.0-beta1.21055.5
2.0.0-beta1.20253.1
@@ -151,9 +151,9 @@
1.0.1-prerelease-00006
16.9.0-preview-20201201-01
- 1.0.0-prerelease.21330.2
- 1.0.0-prerelease.21330.2
- 1.0.1-alpha.0.21330.1
+ 1.0.0-prerelease.21357.4
+ 1.0.0-prerelease.21357.4
+ 1.0.1-alpha.0.21355.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.21330.1
+ 6.0.100-preview.6.21357.1
$(MicrosoftNETILLinkTasksVersion)
6.0.0-preview.7.21328.1
6.0.0-preview.7.21357.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.1
- 11.1.0-alpha.1.21328.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
- 6.0.0-preview.7.21330.1
- $(MicrosoftNETWorkloadEmscriptenManifest60100)
+ 6.0.0-preview.7.21358.1
+ $(MicrosoftNETWorkloadEmscriptenManifest60100Version)
diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1
index a0b5fc37f4388..18823840b1127 100644
--- a/eng/common/SetupNugetSources.ps1
+++ b/eng/common/SetupNugetSources.ps1
@@ -158,4 +158,10 @@ if ($dotnet5Source -ne $null) {
AddPackageSource -Sources $sources -SourceName "dotnet5-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password
}
+$dotnet6Source = $sources.SelectSingleNode("add[@key='dotnet6']")
+if ($dotnet6Source -ne $null) {
+ AddPackageSource -Sources $sources -SourceName "dotnet6-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2" -Creds $creds -Username $userName -Password $Password
+ AddPackageSource -Sources $sources -SourceName "dotnet6-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password
+}
+
$doc.Save($filename)
diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh
index 2734601c13c4b..ad3fb74fd2cc8 100644
--- a/eng/common/SetupNugetSources.sh
+++ b/eng/common/SetupNugetSources.sh
@@ -129,6 +129,30 @@ if [ "$?" == "0" ]; then
PackageSources+=('dotnet5-internal-transport')
fi
+# Ensure dotnet6-internal and dotnet6-internal-transport are in the packageSources if the public dotnet6 feeds are present
+grep -i ""
+
+ sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile
+ fi
+ PackageSources+=('dotnet6-internal')
+
+ grep -i "" $ConfigFile
+ if [ "$?" != "0" ]; then
+ echo "Adding dotnet6-internal-transport to the packageSources."
+ PackageSourcesNodeFooter=""
+ PackageSourceTemplate="${TB}"
+
+ sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile
+ fi
+ PackageSources+=('dotnet6-internal-transport')
+fi
+
# I want things split line by line
PrevIFS=$IFS
IFS=$'\n'
diff --git a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile
index 08adb4359e05c..2a232ee452418 100644
--- a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile
+++ b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile
@@ -1,6 +1,6 @@
# Builds and copies library artifacts into target dotnet sdk image
ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754
-ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim
+ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim
FROM $BUILD_BASE_IMAGE as corefxbuild
diff --git a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile
index dd306fc4ff126..9fcb11a9a0c9e 100644
--- a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile
+++ b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile
@@ -1,6 +1,6 @@
# escape=`
# Simple Dockerfile which copies library build artifacts into target dotnet sdk image
-ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809
+ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809
FROM $SDK_BASE_IMAGE as target
ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost"
diff --git a/eng/docker/libraries-sdk.linux.Dockerfile b/eng/docker/libraries-sdk.linux.Dockerfile
index 1d704ecbc42b3..fd4f071da198d 100644
--- a/eng/docker/libraries-sdk.linux.Dockerfile
+++ b/eng/docker/libraries-sdk.linux.Dockerfile
@@ -1,6 +1,6 @@
# Builds and copies library artifacts into target dotnet sdk image
ARG BUILD_BASE_IMAGE=mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-f39df28-20191023143754
-ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-buster-slim
+ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim
FROM $BUILD_BASE_IMAGE as corefxbuild
diff --git a/eng/docker/libraries-sdk.windows.Dockerfile b/eng/docker/libraries-sdk.windows.Dockerfile
index 564378f446729..c8d993b18d389 100644
--- a/eng/docker/libraries-sdk.windows.Dockerfile
+++ b/eng/docker/libraries-sdk.windows.Dockerfile
@@ -1,6 +1,6 @@
# escape=`
# Simple Dockerfile which copies clr and library build artifacts into target dotnet sdk image
-ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:5.0-nanoserver-1809
+ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-nanoserver-1809
FROM $SDK_BASE_IMAGE as target
ARG TESTHOST_LOCATION=".\\artifacts\\bin\\testhost"
diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets
index 01a0c6aac679a..fb05c25a395a1 100644
--- a/eng/liveBuilds.targets
+++ b/eng/liveBuilds.targets
@@ -109,6 +109,8 @@
true
+
+
diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml
index 96c116eb87272..84f1786bd2171 100644
--- a/eng/pipelines/common/global-build-job.yml
+++ b/eng/pipelines/common/global-build-job.yml
@@ -83,6 +83,12 @@ jobs:
${{ if ne(parameters.isOfficialBuild, true) }}:
value: ''
+ - name: _buildDarwinFrameworksParameter
+ ${{ if in(parameters.osGroup, 'iOS', 'tvOS', 'MacCatalyst')}}:
+ value: /p:BuildDarwinFrameworks=true
+ ${{ if notin(parameters.osGroup, 'iOS', 'tvOS', 'MacCatalyst')}}:
+ value: ''
+
- name: _richCodeNavigationParam
${{ if eq(parameters.enableRichCodeNavigation, true) }}:
value: /p:EnableRichCodeNavigation=true
@@ -134,7 +140,7 @@ jobs:
displayName: Install native dependencies
# Build
- - script: $(_sclEnableCommand) $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam)
+ - script: $(_sclEnableCommand) $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) $(_buildDarwinFrameworksParameter)
displayName: Build product
${{ if eq(parameters.useContinueOnErrorDuringBuild, true) }}:
continueOnError: ${{ parameters.shouldContinueOnError }}
diff --git a/eng/pipelines/libraries/stress/http.yml b/eng/pipelines/libraries/stress/http.yml
index 08b7611d21159..ba3a9f875f9e9 100644
--- a/eng/pipelines/libraries/stress/http.yml
+++ b/eng/pipelines/libraries/stress/http.yml
@@ -25,7 +25,7 @@ variables:
jobs:
- job: linux
displayName: Docker Linux
- timeoutInMinutes: 150
+ timeoutInMinutes: 180
pool:
name: NetCorePublic-Pool
queue: BuildPool.Ubuntu.1804.Amd64.Open
@@ -47,6 +47,14 @@ jobs:
name: buildStress
displayName: Build HttpStress
+ - bash: |
+ cd '$(httpStressProject)'
+ export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 3.0"
+ export HTTPSTRESS_SERVER_ARGS="$HTTPSTRESS_SERVER_ARGS -http 3.0"
+ docker-compose up --abort-on-container-exit --no-color
+ displayName: Run HttpStress - HTTP 3.0
+ condition: and(eq(variables['buildRuntime.succeeded'], 'true'), eq(variables['buildStress.succeeded'], 'true'))
+
- bash: |
cd '$(httpStressProject)'
export HTTPSTRESS_CLIENT_ARGS="$HTTPSTRESS_CLIENT_ARGS -http 2.0"
diff --git a/eng/pipelines/mono/templates/build-job.yml b/eng/pipelines/mono/templates/build-job.yml
index ef9af237168b7..bdb15a09a665e 100644
--- a/eng/pipelines/mono/templates/build-job.yml
+++ b/eng/pipelines/mono/templates/build-job.yml
@@ -63,6 +63,8 @@ jobs:
value: ''
- name: msCorDbi
value: '+mono.mscordbi'
+ - name: darwinFrameworks
+ value: ''
- ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}:
- name: officialBuildIdArg
value: '/p:officialBuildId=$(Build.BuildNumber)'
@@ -76,15 +78,23 @@ jobs:
- ${{ if eq(parameters.osGroup, 'tvOS') }}:
- name: osOverride
value: -os tvOS
+ - name: darwinFrameworks
+ value: /p:BuildDarwinFrameworks=true
- ${{ if eq(parameters.osGroup, 'tvOSSimulator') }}:
- name: osOverride
value: -os tvOSSimulator
+ - name: darwinFrameworks
+ value: /p:BuildDarwinFrameworks=true
- ${{ if eq(parameters.osGroup, 'iOS') }}:
- name: osOverride
value: -os iOS
+ - name: darwinFrameworks
+ value: /p:BuildDarwinFrameworks=true
- ${{ if eq(parameters.osGroup, 'iOSSimulator') }}:
- name: osOverride
value: -os iOSSimulator
+ - name: darwinFrameworks
+ value: /p:BuildDarwinFrameworks=true
- ${{ if eq(parameters.osGroup, 'Android') }}:
- name: osOverride
value: -os Android
@@ -136,7 +146,7 @@ jobs:
# Build
- ${{ if ne(parameters.osGroup, 'windows') }}:
- - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter)
+ - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(darwinFrameworks)
displayName: Build product
- ${{ if eq(parameters.osGroup, 'windows') }}:
- script: build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter)
diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml
index 666641354bf5a..f58caca2899de 100644
--- a/eng/pipelines/runtime-staging.yml
+++ b/eng/pipelines/runtime-staging.yml
@@ -119,7 +119,7 @@ jobs:
jobParameters:
testGroup: innerloop
nameSuffix: AllSubsets_Mono
- buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunAOTCompilation=true /p:MonoForceInterpreter=true
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true
timeoutInMinutes: 180
condition: >-
or(
diff --git a/global.json b/global.json
index ca21d08dc3bb1..99807111962c2 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.21330.3",
+ "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21357.3",
"Microsoft.DotNet.PackageValidation": "1.0.0-preview.7.21352.4",
- "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21330.2",
- "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21330.2",
- "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21330.3",
+ "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.Build.NoTargets": "3.0.4",
"Microsoft.Build.Traversal": "3.0.23",
- "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21321.2"
+ "Microsoft.NET.Sdk.IL": "6.0.0-preview.7.21355.1"
}
}
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/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/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/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp
index 7bebc596f5ec7..4d53d1ed0dfe2 100644
--- a/src/coreclr/gc/windows/gcenv.windows.cpp
+++ b/src/coreclr/gc/windows/gcenv.windows.cpp
@@ -983,6 +983,12 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
MEMORYSTATUSEX memStatus;
GetProcessMemoryLoad(&memStatus);
assert(memStatus.ullTotalPhys != 0);
+
+ // For 32-bit processes the virtual address range could be smaller than the amount of physical
+ // memory on the machine/in the container, we need to restrict by the VM.
+ if (memStatus.ullTotalVirtual < memStatus.ullTotalPhys)
+ return memStatus.ullTotalVirtual;
+
return memStatus.ullTotalPhys;
}
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/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp
index 981879142bf26..ffad32a61b200 100644
--- a/src/coreclr/jit/assertionprop.cpp
+++ b/src/coreclr/jit/assertionprop.cpp
@@ -558,7 +558,7 @@ void Compiler::optAssertionInit(bool isLocalProp)
}
#ifdef DEBUG
-void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* =0 */)
+void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex assertionIndex /* = 0 */)
{
if (curAssertion->op1.kind == O1K_EXACT_TYPE)
{
@@ -590,10 +590,6 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
printf("?assertion classification? ");
}
printf("Assertion: ");
- if (!optLocalAssertionProp)
- {
- printf("(%d, %d) ", curAssertion->op1.vn, curAssertion->op2.vn);
- }
if (!optLocalAssertionProp)
{
@@ -778,12 +774,67 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
if (assertionIndex > 0)
{
- printf(" index=#%02u, mask=", assertionIndex);
- printf("%s", BitVecOps::ToString(apTraits, BitVecOps::MakeSingleton(apTraits, assertionIndex - 1)));
+ printf(", index = ");
+ optPrintAssertionIndex(assertionIndex);
}
printf("\n");
}
+
+void Compiler::optPrintAssertionIndex(AssertionIndex index)
+{
+ if (index == NO_ASSERTION_INDEX)
+ {
+ printf("#NA");
+ return;
+ }
+
+ printf("#%02u", index);
+}
+
+void Compiler::optPrintAssertionIndices(ASSERT_TP assertions)
+{
+ if (BitVecOps::IsEmpty(apTraits, assertions))
+ {
+ optPrintAssertionIndex(NO_ASSERTION_INDEX);
+ return;
+ }
+
+ BitVecOps::Iter iter(apTraits, assertions);
+ unsigned bitIndex = 0;
+ if (iter.NextElem(&bitIndex))
+ {
+ optPrintAssertionIndex(static_cast(bitIndex + 1));
+ while (iter.NextElem(&bitIndex))
+ {
+ printf(" ");
+ optPrintAssertionIndex(static_cast(bitIndex + 1));
+ }
+ }
+}
+#endif // DEBUG
+
+/* static */
+void Compiler::optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer /* = nullptr */)
+{
+#ifdef DEBUG
+ Compiler* compiler = JitTls::GetCompiler();
+ if (compiler->verbose)
+ {
+ printf(header);
+ compiler->optPrintAssertionIndices(assertions);
+ if (footer != nullptr)
+ {
+ printf(footer);
+ }
+ }
#endif // DEBUG
+}
+
+/* static */
+void Compiler::optDumpAssertionIndices(ASSERT_TP assertions, const char* footer /* = nullptr */)
+{
+ optDumpAssertionIndices("", assertions, footer);
+}
/******************************************************************************
*
@@ -1197,13 +1248,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
toType = op2->gtType;
goto SUBRANGE_COMMON;
- case GT_ARR_ELEM:
-
- /* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */
-
- toType = op2->gtType;
- goto SUBRANGE_COMMON;
-
case GT_LCL_FLD:
/* Assigning the result of an indirection into a LCL_VAR, see if we can add a subrange assertion */
@@ -4511,7 +4555,7 @@ void Compiler::optImpliedByConstAssertion(AssertionDsc* constAssertion, ASSERT_T
if (verbose)
{
AssertionDsc* firstAssertion = optGetAssertion(1);
- printf("\nCompiler::optImpliedByConstAssertion: constAssertion #%02d , implies assertion #%02d",
+ printf("Compiler::optImpliedByConstAssertion: const assertion #%02d implies assertion #%02d\n",
(constAssertion - firstAssertion) + 1, (impAssertion - firstAssertion) + 1);
}
#endif
@@ -4721,8 +4765,12 @@ class AssertionPropFlowCallback
// At the start of the merge function of the dataflow equations, initialize premerge state (to detect change.)
void StartMerge(BasicBlock* block)
{
- JITDUMP("AssertionPropCallback::StartMerge: " FMT_BB " in -> %s\n", block->bbNum,
- BitVecOps::ToString(apTraits, block->bbAssertionIn));
+ if (VerboseDataflow())
+ {
+ JITDUMP("StartMerge: " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n");
+ }
+
BitVecOps::Assign(apTraits, preMergeOut, block->bbAssertionOut);
BitVecOps::Assign(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum]);
}
@@ -4743,11 +4791,14 @@ class AssertionPropFlowCallback
assert(predBlock->bbNext == block);
BitVecOps::IntersectionD(apTraits, pAssertionOut, predBlock->bbAssertionOut);
- JITDUMP("AssertionPropCallback::Merge : Duplicate flow, " FMT_BB " in -> %s, predBlock " FMT_BB
- " out1 -> %s, out2 -> %s\n",
- block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum,
- BitVecOps::ToString(apTraits, mJumpDestOut[predBlock->bbNum]),
- BitVecOps::ToString(apTraits, predBlock->bbAssertionOut));
+ if (VerboseDataflow())
+ {
+ JITDUMP("Merge : Duplicate flow, " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; ");
+ JITDUMP("pred " FMT_BB " ", predBlock->bbNum);
+ Compiler::optDumpAssertionIndices("out1 -> ", mJumpDestOut[predBlock->bbNum], "; ");
+ Compiler::optDumpAssertionIndices("out2 -> ", predBlock->bbAssertionOut, "\n");
+ }
}
}
else
@@ -4755,9 +4806,14 @@ class AssertionPropFlowCallback
pAssertionOut = predBlock->bbAssertionOut;
}
- JITDUMP("AssertionPropCallback::Merge : " FMT_BB " in -> %s, predBlock " FMT_BB " out -> %s\n",
- block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), predBlock->bbNum,
- BitVecOps::ToString(apTraits, pAssertionOut));
+ if (VerboseDataflow())
+ {
+ JITDUMP("Merge : " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; ");
+ JITDUMP("pred " FMT_BB " ", predBlock->bbNum);
+ Compiler::optDumpAssertionIndices("out -> ", pAssertionOut, "\n");
+ }
+
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, pAssertionOut);
}
@@ -4781,8 +4837,11 @@ class AssertionPropFlowCallback
// At the end of the merge store results of the dataflow equations, in a postmerge state.
bool EndMerge(BasicBlock* block)
{
- JITDUMP("AssertionPropCallback::EndMerge : " FMT_BB " in -> %s\n\n", block->bbNum,
- BitVecOps::ToString(apTraits, block->bbAssertionIn));
+ if (VerboseDataflow())
+ {
+ JITDUMP("EndMerge : " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "\n\n");
+ }
BitVecOps::DataFlowD(apTraits, block->bbAssertionOut, block->bbAssertionGen, block->bbAssertionIn);
BitVecOps::DataFlowD(apTraits, mJumpDestOut[block->bbNum], mJumpDestGen[block->bbNum], block->bbAssertionIn);
@@ -4790,24 +4849,35 @@ class AssertionPropFlowCallback
bool changed = (!BitVecOps::Equal(apTraits, preMergeOut, block->bbAssertionOut) ||
!BitVecOps::Equal(apTraits, preMergeJumpDestOut, mJumpDestOut[block->bbNum]));
- if (changed)
- {
- JITDUMP("AssertionPropCallback::Changed : " FMT_BB " before out -> %s; after out -> %s;\n"
- "\t\tjumpDest before out -> %s; jumpDest after out -> %s;\n\n",
- block->bbNum, BitVecOps::ToString(apTraits, preMergeOut),
- BitVecOps::ToString(apTraits, block->bbAssertionOut),
- BitVecOps::ToString(apTraits, preMergeJumpDestOut),
- BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum]));
- }
- else
+ if (VerboseDataflow())
{
- JITDUMP("AssertionPropCallback::Unchanged : " FMT_BB " out -> %s; \t\tjumpDest out -> %s\n\n",
- block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionOut),
- BitVecOps::ToString(apTraits, mJumpDestOut[block->bbNum]));
+ if (changed)
+ {
+ JITDUMP("Changed : " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("before out -> ", preMergeOut, "; ");
+ Compiler::optDumpAssertionIndices("after out -> ", block->bbAssertionOut, ";\n ");
+ Compiler::optDumpAssertionIndices("jumpDest before out -> ", preMergeJumpDestOut, "; ");
+ Compiler::optDumpAssertionIndices("jumpDest after out -> ", mJumpDestOut[block->bbNum], ";\n\n");
+ }
+ else
+ {
+ JITDUMP("Unchanged : " FMT_BB " ", block->bbNum);
+ Compiler::optDumpAssertionIndices("out -> ", block->bbAssertionOut, "; ");
+ Compiler::optDumpAssertionIndices("jumpDest out -> ", mJumpDestOut[block->bbNum], "\n\n");
+ }
}
return changed;
}
+
+ // Can be enabled to get detailed debug output about dataflow for assertions.
+ bool VerboseDataflow()
+ {
+#if 0
+ return VERBOSE;
+#endif
+ return false;
+ }
};
/*****************************************************************************
@@ -4894,16 +4964,28 @@ ASSERT_TP* Compiler::optComputeAssertionGen()
#ifdef DEBUG
if (verbose)
{
- printf(FMT_BB " valueGen = %s", block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionGen));
+ if (block == fgFirstBB)
+ {
+ printf("\n");
+ }
+
+ printf(FMT_BB " valueGen = ", block->bbNum);
+ optPrintAssertionIndices(block->bbAssertionGen);
if (block->bbJumpKind == BBJ_COND)
{
- printf(" => " FMT_BB " valueGen = %s,", block->bbJumpDest->bbNum,
- BitVecOps::ToString(apTraits, jumpDestGen[block->bbNum]));
+ printf(" => " FMT_BB " valueGen = ", block->bbJumpDest->bbNum);
+ optPrintAssertionIndices(jumpDestGen[block->bbNum]);
}
printf("\n");
+
+ if (block == fgLastBB)
+ {
+ printf("\n");
+ }
}
#endif
}
+
return jumpDestGen;
}
@@ -5408,6 +5490,10 @@ void Compiler::optAssertionPropMain()
// Modified dataflow algorithm for available expressions.
DataFlow flow(this);
AssertionPropFlowCallback ap(this, bbJtrueAssertionOut, jumpDestGen);
+ if (ap.VerboseDataflow())
+ {
+ JITDUMP("AssertionPropFlowCallback:\n\n")
+ }
flow.ForwardAnalysis(ap);
for (BasicBlock* const block : Blocks())
@@ -5419,16 +5505,15 @@ void Compiler::optAssertionPropMain()
#ifdef DEBUG
if (verbose)
{
- printf("\n");
for (BasicBlock* const block : Blocks())
{
- printf("\n" FMT_BB, block->bbNum);
- printf(" valueIn = %s", BitVecOps::ToString(apTraits, block->bbAssertionIn));
- printf(" valueOut = %s", BitVecOps::ToString(apTraits, block->bbAssertionOut));
+ printf(FMT_BB ":\n", block->bbNum);
+ optDumpAssertionIndices(" in = ", block->bbAssertionIn, "\n");
+ optDumpAssertionIndices(" out = ", block->bbAssertionOut, "\n");
if (block->bbJumpKind == BBJ_COND)
{
- printf(" => " FMT_BB, block->bbJumpDest->bbNum);
- printf(" valueOut= %s", BitVecOps::ToString(apTraits, bbJtrueAssertionOut[block->bbNum]));
+ printf(" " FMT_BB " = ", block->bbJumpDest->bbNum);
+ optDumpAssertionIndices(bbJtrueAssertionOut[block->bbNum], "\n");
}
}
printf("\n");
@@ -5473,9 +5558,11 @@ void Compiler::optAssertionPropMain()
// and thus we must morph, set order, re-link
for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
- JITDUMP("Propagating %s assertions for " FMT_BB ", stmt " FMT_STMT ", tree [%06d], tree -> %d\n",
- BitVecOps::ToString(apTraits, assertions), block->bbNum, stmt->GetID(), dspTreeID(tree),
- tree->GetAssertionInfo().GetAssertionIndex());
+ optDumpAssertionIndices("Propagating ", assertions, " ");
+ JITDUMP("for " FMT_BB ", stmt " FMT_STMT ", tree [%06d]", block->bbNum, stmt->GetID(), dspTreeID(tree));
+ JITDUMP(", tree -> ");
+ JITDUMPEXEC(optPrintAssertionIndex(tree->GetAssertionInfo().GetAssertionIndex()));
+ JITDUMP("\n");
GenTree* newTree = optAssertionProp(assertions, tree, stmt, block);
if (newTree)
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 78926e64fda65..d2e08159ec104 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -552,9 +552,9 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo
else
{
// If this is going live, the register must not have a variable in it, except
- // in the case of an exception variable, which may be already treated as live
- // in the register.
- assert(varDsc->lvLiveInOutOfHndlr || ((regSet.GetMaskVars() & regMask) == 0));
+ // in the case of an exception or "spill at single-def" variable, which may be already treated
+ // as live in the register.
+ assert(varDsc->IsAlwaysAliveInMemory() || ((regSet.GetMaskVars() & regMask) == 0));
regSet.AddMaskVars(regMask);
}
}
@@ -736,7 +736,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
bool isGCRef = (varDsc->TypeGet() == TYP_REF);
bool isByRef = (varDsc->TypeGet() == TYP_BYREF);
bool isInReg = varDsc->lvIsInReg();
- bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr;
+ bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory();
if (isInReg)
{
@@ -777,8 +777,8 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
if (varDsc->lvIsInReg())
{
// If this variable is going live in a register, it is no longer live on the stack,
- // unless it is an EH var, which always remains live on the stack.
- if (!varDsc->lvLiveInOutOfHndlr)
+ // unless it is an EH/"spill at single-def" var, which always remains live on the stack.
+ if (!varDsc->IsAlwaysAliveInMemory())
{
#ifdef DEBUG
if (VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex))
@@ -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);
}
@@ -4355,16 +4354,10 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
#endif // defined(UNIX_AMD64_ABI)
noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg);
-#ifndef TARGET_64BIT
-#ifndef TARGET_ARM
- // Right now we think that incoming arguments are not pointer sized. When we eventually
- // understand the calling convention, this still won't be true. But maybe we'll have a better
- // idea of how to ignore it.
-
- // On Arm, a long can be passed in register
- noway_assert(genTypeSize(genActualType(varDsc->TypeGet())) == TARGET_POINTER_SIZE);
-#endif
-#endif // TARGET_64BIT
+#ifdef TARGET_X86
+ // On x86 we don't enregister args that are not pointer sized.
+ noway_assert(genTypeSize(varDsc->GetActualRegisterType()) == TARGET_POINTER_SIZE);
+#endif // TARGET_X86
noway_assert(varDsc->lvIsInReg() && !regArgTab[argNum].circular);
@@ -11428,7 +11421,7 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode)
{
varReg = REG_STK;
}
- if ((varReg == REG_STK) || fieldVarDsc->lvLiveInOutOfHndlr)
+ if ((varReg == REG_STK) || fieldVarDsc->IsAlwaysAliveInMemory())
{
if (!lclNode->AsLclVar()->IsLastUse(i))
{
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index 8bf1f852ffd4d..aa2fb0f58955f 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -229,7 +229,7 @@ void CodeGen::genCodeForBBlist()
{
newRegByrefSet |= varDsc->lvRegMask();
}
- if (!varDsc->lvLiveInOutOfHndlr)
+ if (!varDsc->IsAlwaysAliveInMemory())
{
#ifdef DEBUG
if (verbose && VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex))
@@ -240,7 +240,7 @@ void CodeGen::genCodeForBBlist()
VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex);
}
}
- if ((!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr) && compiler->lvaIsGCTracked(varDsc))
+ if ((!varDsc->lvIsInReg() || varDsc->IsAlwaysAliveInMemory()) && compiler->lvaIsGCTracked(varDsc))
{
#ifdef DEBUG
if (verbose && !VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex))
@@ -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)
@@ -873,9 +873,9 @@ void CodeGen::genSpillVar(GenTree* tree)
var_types lclType = varDsc->GetActualRegisterType();
emitAttr size = emitTypeSize(lclType);
- // If this is a write-thru variable, we don't actually spill at a use, but we will kill the var in the reg
- // (below).
- if (!varDsc->lvLiveInOutOfHndlr)
+ // If this is a write-thru or a single-def variable, we don't actually spill at a use,
+ // but we will kill the var in the reg (below).
+ if (!varDsc->IsAlwaysAliveInMemory())
{
instruction storeIns = ins_Store(lclType, compiler->isSIMDTypeLocalAligned(varNum));
assert(varDsc->GetRegNum() == tree->GetRegNum());
@@ -883,7 +883,7 @@ void CodeGen::genSpillVar(GenTree* tree)
}
// We should only have both GTF_SPILL (i.e. the flag causing this method to be called) and
- // GTF_SPILLED on a write-thru def, for which we should not be calling this method.
+ // GTF_SPILLED on a write-thru/single-def def, for which we should not be calling this method.
assert((tree->gtFlags & GTF_SPILLED) == 0);
// Remove the live var from the register.
@@ -918,8 +918,9 @@ void CodeGen::genSpillVar(GenTree* tree)
}
else
{
- // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar.
- assert(varDsc->lvLiveInOutOfHndlr && ((tree->gtFlags & GTF_VAR_DEF) != 0));
+ // We only have 'GTF_SPILL' and 'GTF_SPILLED' on a def of a write-thru lclVar
+ // or a single-def var that is to be spilled at its definition.
+ assert((varDsc->IsAlwaysAliveInMemory()) && ((tree->gtFlags & GTF_VAR_DEF) != 0));
}
#ifdef USING_VARIABLE_LIVE_RANGE
@@ -1055,7 +1056,7 @@ void CodeGen::genUnspillLocal(
}
#endif // USING_VARIABLE_LIVE_RANGE
- if (!varDsc->lvLiveInOutOfHndlr)
+ if (!varDsc->IsAlwaysAliveInMemory())
{
#ifdef DEBUG
if (VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex))
@@ -1191,12 +1192,12 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
assert(spillType != TYP_UNDEF);
// TODO-Cleanup: The following code could probably be further merged and cleaned up.
-#ifdef TARGET_XARCH
+#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
// Load local variable from its home location.
// In most cases the tree type will indicate the correct type to use for the load.
// However, if it is NOT a normalizeOnLoad lclVar (i.e. NOT a small int that always gets
- // widened when loaded into a register), and its size is not the same as genActualType of
- // the type of the lclVar, then we need to change the type of the tree node when loading.
+ // widened when loaded into a register), and its size is not the same as the actual register type
+ // of the lclVar, then we need to change the type of the tree node when loading.
// This situation happens due to "optimizations" that avoid a cast and
// simply retype the node when using long type lclVar as an int.
// While loading the int in that case would work for this use of the lclVar, if it is
@@ -1210,13 +1211,6 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
assert(!varTypeIsGC(varDsc));
spillType = lclActualType;
}
-#elif defined(TARGET_ARM64)
- var_types targetType = unspillTree->gtType;
- if (spillType != genActualType(varDsc->lvType) && !varTypeIsGC(spillType) && !varDsc->lvNormalizeOnLoad())
- {
- assert(!varTypeIsGC(varDsc));
- spillType = genActualType(varDsc->lvType);
- }
#elif defined(TARGET_ARM)
// No normalizing for ARM
#else
@@ -1465,7 +1459,8 @@ regNumber CodeGen::genConsumeReg(GenTree* tree)
LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()];
if (varDsc->GetRegNum() != REG_STK)
{
- inst_Mov(tree->TypeGet(), tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true);
+ var_types regType = varDsc->GetRegisterType(lcl);
+ inst_Mov(regType, tree->GetRegNum(), varDsc->GetRegNum(), /* canSkip */ true);
}
}
@@ -2050,12 +2045,12 @@ void CodeGen::genSpillLocal(unsigned varNum, var_types type, GenTreeLclVar* lclN
// We have a register candidate local that is marked with GTF_SPILL.
// This flag generally means that we need to spill this local.
- // The exception is the case of a use of an EH var use that is being "spilled"
+ // The exception is the case of a use of an EH/spill-at-single-def var use that is being "spilled"
// to the stack, indicated by GTF_SPILL (note that all EH lclVar defs are always
- // spilled, i.e. write-thru).
- // An EH var use is always valid on the stack (so we don't need to actually spill it),
+ // spilled, i.e. write-thru. Likewise, single-def vars that are spilled at its definitions).
+ // An EH or single-def var use is always valid on the stack (so we don't need to actually spill it),
// but the GTF_SPILL flag records the fact that the register value is going dead.
- if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || !varDsc->lvLiveInOutOfHndlr)
+ if (((lclNode->gtFlags & GTF_VAR_DEF) != 0) || (!varDsc->IsAlwaysAliveInMemory()))
{
// Store local variable to its home location.
// Ensure that lclVar stores are typed correctly.
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 7019d8afad6c5..bf49e63ab0cbb 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;
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 356fe6498d166..b60353d8e8440 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -446,10 +446,19 @@ class LclVarDsc
// before lvaMarkLocalVars: identifies ref type locals that can get type updates
// after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies
- unsigned char lvEhWriteThruCandidate : 1; // variable has a single def and hence is a register candidate if
- // if it is an EH variable
+ unsigned char lvSingleDefRegCandidate : 1; // variable has a single def and hence is a register candidate
+ // Currently, this is only used to decide if an EH variable can be
+ // a register candiate or not.
- unsigned char lvDisqualifyForEhWriteThru : 1; // tracks variable that are disqualified from register candidancy
+ unsigned char lvDisqualifySingleDefRegCandidate : 1; // tracks variable that are disqualified from register
+ // candidancy
+
+ unsigned char lvSpillAtSingleDef : 1; // variable has a single def (as determined by LSRA interval scan)
+ // and is spilled making it candidate to spill right after the
+ // first (and only) definition.
+ // Note: We cannot reuse lvSingleDefRegCandidate because it is set
+ // in earlier phase and the information might not be appropriate
+ // in LSRA.
#if ASSERTION_PROP
unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization
@@ -547,7 +556,7 @@ class LclVarDsc
unsigned char lvFldOrdinal;
#ifdef DEBUG
- unsigned char lvDisqualifyEHVarReason = 'H';
+ unsigned char lvSingleDefDisqualifyReason = 'H';
#endif
#if FEATURE_MULTIREG_ARGS
@@ -1016,11 +1025,30 @@ class LclVarDsc
var_types GetActualRegisterType() const;
- bool IsEnregisterable() const
+ bool IsEnregisterableType() const
{
return GetRegisterType() != TYP_UNDEF;
}
+ bool IsEnregisterableLcl() const
+ {
+ if (lvDoNotEnregister)
+ {
+ return false;
+ }
+ return IsEnregisterableType();
+ }
+
+ //-----------------------------------------------------------------------------
+ // IsAlwaysAliveInMemory: Determines if this variable's value is always
+ // up-to-date on stack. This is possible if this is an EH-var or
+ // we decided to spill after single-def.
+ //
+ bool IsAlwaysAliveInMemory() const
+ {
+ return lvLiveInOutOfHndlr || lvSpillAtSingleDef;
+ }
+
bool CanBeReplacedWithItsField(Compiler* comp) const;
#ifdef DEBUG
@@ -6209,9 +6237,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:
@@ -6700,7 +6728,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.
@@ -6710,6 +6738,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 */
@@ -6844,9 +6903,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();
@@ -6854,16 +6916,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
@@ -6888,6 +6949,7 @@ class Compiler
bool optConfigDisableCSE();
bool optConfigDisableCSE2();
#endif
+
void optOptimizeCSEs();
struct isVarAssgDsc
@@ -7491,9 +7553,14 @@ class Compiler
#ifdef DEBUG
void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex = 0);
+ void optPrintAssertionIndex(AssertionIndex index);
+ void optPrintAssertionIndices(ASSERT_TP assertions);
void optDebugCheckAssertion(AssertionDsc* assertion);
void optDebugCheckAssertions(AssertionIndex AssertionIndex);
#endif
+ static void optDumpAssertionIndices(const char* header, ASSERT_TP assertions, const char* footer = nullptr);
+ static void optDumpAssertionIndices(ASSERT_TP assertions, const char* footer = nullptr);
+
void optAddCopies();
#endif // ASSERTION_PROP
@@ -9304,6 +9371,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/copyprop.cpp b/src/coreclr/jit/copyprop.cpp
index c0d72123e1a11..625e2eee5e8b0 100644
--- a/src/coreclr/jit/copyprop.cpp
+++ b/src/coreclr/jit/copyprop.cpp
@@ -270,9 +270,9 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc
{
JITDUMP("VN based copy assertion for ");
printTreeID(tree);
- printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative));
+ printf(" V%02d " FMT_VN " by ", lclNum, tree->GetVN(VNK_Conservative));
printTreeID(op);
- printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative));
+ printf(" V%02d " FMT_VN ".\n", newLclNum, op->GetVN(VNK_Conservative));
gtDispTree(tree, nullptr, nullptr, true);
}
#endif
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index 352d1b2acfcd7..ca6cd0c4a7133 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,
@@ -1023,9 +1023,9 @@ void emitter::emitBegFN(bool hasFramePtr
emitIGbuffSize = 0;
#if FEATURE_LOOP_ALIGN
- emitLastAlignedIgNum = 0;
- emitLastInnerLoopStartIgNum = 0;
- emitLastInnerLoopEndIgNum = 0;
+ emitLastAlignedIgNum = 0;
+ emitLastLoopStart = 0;
+ emitLastLoopEnd = 0;
#endif
/* Record stack frame info (the temp size is just an estimate) */
@@ -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++;
@@ -4820,58 +4823,112 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG
// if currIG has back-edge to dstIG.
//
// Notes:
-// If the current loop encloses a loop that is already marked as align, then remove
-// the alignment flag present on IG before dstIG.
+// Despite we align only inner most loop, we might see intersected loops because of control flow
+// re-arrangement like adding a split edge in LSRA.
+//
+// If there is an intersection of current loop with last loop that is already marked as align,
+// then *do not align* one of the loop that completely encloses the other one. Or if they both intersect,
+// then *do not align* either of them because since the flow is complicated enough that aligning one of them
+// will not improve the performance.
//
void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock)
{
- insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie;
+ insGroup* dstIG = (insGroup*)loopTopBlock->bbEmitCookie;
+ bool alignCurrentLoop = true;
+ bool alignLastLoop = true;
// With (dstIG != nullptr), ensure that only back edges are tracked.
// If there is forward jump, dstIG is not yet generated.
//
// We don't rely on (block->bbJumpDest->bbNum <= block->bbNum) because the basic
// block numbering is not guaranteed to be sequential.
-
if ((dstIG != nullptr) && (dstIG->igNum <= emitCurIG->igNum))
{
unsigned currLoopStart = dstIG->igNum;
unsigned currLoopEnd = emitCurIG->igNum;
// Only mark back-edge if current loop starts after the last inner loop ended.
- if (emitLastInnerLoopEndIgNum < currLoopStart)
+ if (emitLastLoopEnd < currLoopStart)
{
emitCurIG->igLoopBackEdge = dstIG;
JITDUMP("** IG%02u jumps back to IG%02u forming a loop.\n", currLoopEnd, currLoopStart);
- emitLastInnerLoopStartIgNum = currLoopStart;
- emitLastInnerLoopEndIgNum = currLoopEnd;
+ emitLastLoopStart = currLoopStart;
+ emitLastLoopEnd = currLoopEnd;
+ }
+ else if (currLoopStart == emitLastLoopStart)
+ {
+ // Note: If current and last loop starts at same point,
+ // retain the alignment flag of the smaller loop.
+ // |
+ // .---->|<----.
+ // last | | |
+ // loop | | | current
+ // .---->| | loop
+ // | |
+ // |-----.
+ //
+ }
+ else if ((currLoopStart < emitLastLoopStart) && (emitLastLoopEnd < currLoopEnd))
+ {
+ // if current loop completely encloses last loop,
+ // then current loop should not be aligned.
+ alignCurrentLoop = false;
+ }
+ else if ((emitLastLoopStart < currLoopStart) && (currLoopEnd < emitLastLoopEnd))
+ {
+ // if last loop completely encloses current loop,
+ // then last loop should not be aligned.
+ alignLastLoop = false;
+ }
+ else
+ {
+ // The loops intersect and should not align either of the loops
+ alignLastLoop = false;
+ alignCurrentLoop = false;
}
- // Otherwise, mark the dstIG->prevIG as no alignment needed.
- //
- // Note: If current loop's back-edge target is same as emitLastInnerLoopStartIgNum,
- // retain the alignment flag of dstIG->prevIG so the loop
- // (emitLastInnerLoopStartIgNum ~ emitLastInnerLoopEndIgNum) is still aligned.
- else if (emitLastInnerLoopStartIgNum != currLoopStart)
- {
- // Find the IG before dstIG...
- instrDescAlign* alignInstr = emitAlignList;
- while ((alignInstr != nullptr) && (alignInstr->idaIG->igNext != dstIG))
- {
- alignInstr = alignInstr->idaNext;
- }
- // ...and clear the IGF_LOOP_ALIGN flag
- if (alignInstr != nullptr)
+ if (!alignLastLoop || !alignCurrentLoop)
+ {
+ instrDescAlign* alignInstr = emitAlignList;
+ bool markedLastLoop = alignLastLoop;
+ bool markedCurrLoop = alignCurrentLoop;
+ while ((alignInstr != nullptr))
{
- assert(alignInstr->idaIG->igNext == dstIG);
- alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN;
+ // Find the IG before current loop and clear the IGF_LOOP_ALIGN flag
+ if (!alignCurrentLoop && (alignInstr->idaIG->igNext == dstIG))
+ {
+ assert(!markedCurrLoop);
+ alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN;
+ markedCurrLoop = true;
+ JITDUMP("** Skip alignment for current loop IG%02u ~ IG%02u because it encloses an aligned loop "
+ "IG%02u ~ IG%02u.\n",
+ currLoopStart, currLoopEnd, emitLastLoopStart, emitLastLoopEnd);
+ }
+
+ // Find the IG before the last loop and clear the IGF_LOOP_ALIGN flag
+ if (!alignLastLoop && (alignInstr->idaIG->igNext != nullptr) &&
+ (alignInstr->idaIG->igNext->igNum == emitLastLoopStart))
+ {
+ assert(!markedLastLoop);
+ assert(alignInstr->idaIG->isLoopAlign());
+ alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN;
+ markedLastLoop = true;
+ JITDUMP("** Skip alignment for aligned loop IG%02u ~ IG%02u because it encloses the current loop "
+ "IG%02u ~ IG%02u.\n",
+ emitLastLoopStart, emitLastLoopEnd, currLoopStart, currLoopEnd);
+ }
+
+ if (markedLastLoop && markedCurrLoop)
+ {
+ break;
+ }
+
+ alignInstr = alignInstr->idaNext;
}
- JITDUMP(
- "** Skip alignment for loop IG%02u ~ IG%02u, because it encloses an aligned loop IG%02u ~ IG%02u.\n",
- currLoopStart, currLoopEnd, emitLastInnerLoopStartIgNum, emitLastInnerLoopEndIgNum);
+ assert(markedLastLoop && markedCurrLoop);
}
}
}
@@ -4973,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
@@ -4985,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;
}
@@ -5045,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;
}
@@ -5070,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;
}
@@ -5097,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);
}
}
@@ -5129,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);
}
}
}
@@ -5158,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
@@ -5846,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));
@@ -5963,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
@@ -6808,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)
{
@@ -7932,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;
@@ -7974,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.
@@ -7986,6 +8127,12 @@ void emitter::emitInitIG(insGroup* ig)
#if FEATURE_LOOP_ALIGN
ig->igLoopBackEdge = nullptr;
#endif
+
+#ifdef DEBUG
+ ig->lastGeneratedBlock = nullptr;
+ // Explicitly call the constructor, since IGs don't actually have a constructor.
+ ig->igBlocks.jitstd::list::list(emitComp->getAllocator(CMK_LoopOpt));
+#endif
}
/*****************************************************************************
@@ -8050,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
}
/*****************************************************************************
@@ -8584,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 666201fc98bf0..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
@@ -1762,12 +1769,12 @@ class emitter
void emitJumpDistBind(); // Bind all the local jumps in method
#if FEATURE_LOOP_ALIGN
- instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG
- unsigned emitLastInnerLoopStartIgNum; // Start IG of last inner loop
- unsigned emitLastInnerLoopEndIgNum; // End IG of last inner loop
- unsigned emitLastAlignedIgNum; // last IG that has align instruction
- instrDescAlign* emitAlignList; // list of local align instructions in method
- instrDescAlign* emitAlignLast; // last align instruction in method
+ instrDescAlign* emitCurIGAlignList; // list of align instructions in current IG
+ unsigned emitLastLoopStart; // Start IG of last inner loop
+ unsigned emitLastLoopEnd; // End IG of last inner loop
+ unsigned emitLastAlignedIgNum; // last IG that has align instruction
+ instrDescAlign* emitAlignList; // list of local align instructions in method
+ instrDescAlign* emitAlignLast; // last align instruction in method
unsigned getLoopSize(insGroup* igLoopHeader,
unsigned maxLoopSize DEBUG_ARG(bool isAlignAdjusted)); // Get the smallest loop size
void emitLoopAlignment();
@@ -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 a0a5e3283d4e9..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);
}
@@ -296,6 +296,13 @@ bool emitter::AreUpper32BitsZero(regNumber reg)
return false;
}
+#ifdef TARGET_AMD64
+ if (id->idIns() == INS_movsxd)
+ {
+ return false;
+ }
+#endif
+
// movzx always zeroes the upper 32 bits.
if (id->idIns() == INS_movzx)
{
@@ -438,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
@@ -514,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
@@ -4292,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.
@@ -8452,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);
}
}
@@ -8684,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(" ");
}
@@ -9649,7 +9682,7 @@ void emitter::emitDispIns(
}
else
{
- printf("G_M%03u_IG%02u", emitComp->compMethodID, id->idAddr()->iiaIGlabel->igNum);
+ emitPrintLabel(id->idAddr()->iiaIGlabel);
}
}
else
@@ -14669,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/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/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp
index 1b4d50fe875c5..cc8e1cd585f84 100644
--- a/src/coreclr/jit/gcinfo.cpp
+++ b/src/coreclr/jit/gcinfo.cpp
@@ -272,7 +272,6 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree
case GT_LEA:
return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base());
- case GT_ARR_ELEM: /* Definitely in the managed heap */
case GT_CLS_VAR:
return WBF_BarrierUnchecked;
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index b56372d18d28f..277cd53e1c10b 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -6423,6 +6423,9 @@ GenTreeCall* Compiler::gtNewCallNode(
GenTreeCall* node = new (this, GT_CALL) GenTreeCall(genActualType(type));
node->gtFlags |= (GTF_CALL | GTF_GLOB_REF);
+#ifdef UNIX_X86_ABI
+ node->gtFlags |= GTF_CALL_POP_ARGS;
+#endif // UNIX_X86_ABI
for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
node->gtFlags |= (use.GetNode()->gtFlags & GTF_ALL_EFFECT);
@@ -10291,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
{
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index b043956468848..4aeeeb9e48633 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)
@@ -5366,9 +5367,9 @@ struct GenTreeBoundsChk : public GenTree
}
};
-// gtArrElem -- general array element (GT_ARR_ELEM), for non "SZ_ARRAYS"
-// -- multidimensional arrays, or 1-d arrays with non-zero lower bounds.
-
+// GenTreeArrElem - bounds checked address (byref) of a general array element,
+// for multidimensional arrays, or 1-d arrays with non-zero lower bounds.
+//
struct GenTreeArrElem : public GenTree
{
GenTree* gtArrObj;
@@ -5384,7 +5385,7 @@ struct GenTreeArrElem : public GenTree
// This has caused VSW 571394.
var_types gtArrElemType; // The array element type
- // Requires that "inds" is a pointer to an array of "rank" GenTreePtrs for the indices.
+ // Requires that "inds" is a pointer to an array of "rank" nodes for the indices.
GenTreeArrElem(
var_types type, GenTree* arr, unsigned char rank, unsigned char elemSize, var_types elemType, GenTree** inds)
: GenTree(GT_ARR_ELEM, type), gtArrObj(arr), gtArrRank(rank), gtArrElemSize(elemSize), gtArrElemType(elemType)
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/jit.h b/src/coreclr/jit/jit.h
index 2dd39559c282e..fb39fda019c45 100644
--- a/src/coreclr/jit/jit.h
+++ b/src/coreclr/jit/jit.h
@@ -487,6 +487,11 @@ const bool dspGCtbls = true;
if (JitTls::GetCompiler()->verbose) \
logf(__VA_ARGS__); \
}
+#define JITDUMPEXEC(x) \
+ { \
+ if (JitTls::GetCompiler()->verbose) \
+ x; \
+ }
#define JITLOG(x) \
{ \
JitLogEE x; \
@@ -521,6 +526,7 @@ const bool dspGCtbls = true;
#define VERBOSE JitTls::GetCompiler()->verbose
#else // !DEBUG
#define JITDUMP(...)
+#define JITDUMPEXEC(x)
#define JITLOG(x)
#define JITLOG_THIS(t, x)
#define DBEXEC(flg, expr)
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index cc10adc0b8809..b14597a870298 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
@@ -552,8 +555,12 @@ CONFIG_INTEGER(JitSaveFpLrWithCalleeSavedRegisters, W("JitSaveFpLrWithCalleeSave
#endif // defined(TARGET_ARM64)
#endif // DEBUG
-// Allow to enregister locals with struct type.
-CONFIG_INTEGER(JitEnregStructLocals, W("JitEnregStructLocals"), 0)
+#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
+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
+ // yet.
+#endif
#undef CONFIG_INTEGER
#undef CONFIG_STRING
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/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 603c6747c215c..33c5abaa30df7 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -2559,7 +2559,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
noway_assert(lvaTable[i].lvIsStructField);
lvaTable[i].lvLiveInOutOfHndlr = 1;
// For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
- if (!lvaEnregEHVars || !lvaTable[i].lvEhWriteThruCandidate || lvaTable[i].lvRefCnt() <= 1)
+ if (!lvaEnregEHVars || !lvaTable[i].lvSingleDefRegCandidate || lvaTable[i].lvRefCnt() <= 1)
{
lvaSetVarDoNotEnregister(i DEBUGARG(DNER_LiveInOutOfHandler));
}
@@ -2567,7 +2567,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
}
// For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
- if (!lvaEnregEHVars || !varDsc->lvEhWriteThruCandidate || varDsc->lvRefCnt() <= 1)
+ if (!lvaEnregEHVars || !varDsc->lvSingleDefRegCandidate || varDsc->lvRefCnt() <= 1)
{
lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LiveInOutOfHandler));
}
@@ -3493,7 +3493,7 @@ void Compiler::lvaSortByRefCount()
{
varDsc->lvTracked = 0;
}
- else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct)
+ else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct && !compEnregStructLocals())
{
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
}
@@ -4110,33 +4110,35 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
}
}
- if (!varDsc->lvDisqualifyForEhWriteThru) // If this EH var already disqualified, we can skip this
+ if (!varDsc->lvDisqualifySingleDefRegCandidate) // If this var is already disqualified, we can skip this
{
if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable
{
- bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0;
- bool bbIsReturn = block->bbJumpKind == BBJ_RETURN;
+ bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0;
+ bool bbIsReturn = block->bbJumpKind == BBJ_RETURN;
+ // TODO: Zero-inits in LSRA are created with below condition. Try to use similar condition here as well.
+ // if (compiler->info.compInitMem || varTypeIsGC(varDsc->TypeGet()))
bool needsExplicitZeroInit = fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn);
- if (varDsc->lvEhWriteThruCandidate || needsExplicitZeroInit)
+ if (varDsc->lvSingleDefRegCandidate || needsExplicitZeroInit)
{
#ifdef DEBUG
if (needsExplicitZeroInit)
{
- varDsc->lvDisqualifyEHVarReason = 'Z';
- JITDUMP("EH Var V%02u needs explicit zero init. Disqualified as a register candidate.\n",
+ varDsc->lvSingleDefDisqualifyReason = 'Z';
+ JITDUMP("V%02u needs explicit zero init. Disqualified as a single-def register candidate.\n",
lclNum);
}
else
{
- varDsc->lvDisqualifyEHVarReason = 'M';
- JITDUMP("EH Var V%02u has multiple definitions. Disqualified as a register candidate.\n",
+ varDsc->lvSingleDefDisqualifyReason = 'M';
+ JITDUMP("V%02u has multiple definitions. Disqualified as a single-def register candidate.\n",
lclNum);
}
#endif // DEBUG
- varDsc->lvEhWriteThruCandidate = false;
- varDsc->lvDisqualifyForEhWriteThru = true;
+ varDsc->lvSingleDefRegCandidate = false;
+ varDsc->lvDisqualifySingleDefRegCandidate = true;
}
else
{
@@ -4146,7 +4148,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
if (!varTypeNeedsPartialCalleeSave(varDsc->lvType))
#endif
{
- varDsc->lvEhWriteThruCandidate = true;
+ varDsc->lvSingleDefRegCandidate = true;
JITDUMP("Marking EH Var V%02u as a register candidate.\n", lclNum);
}
}
@@ -4521,8 +4523,8 @@ void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
// that was set by past phases.
if (!isRecompute)
{
- varDsc->lvSingleDef = varDsc->lvIsParam;
- varDsc->lvEhWriteThruCandidate = varDsc->lvIsParam;
+ varDsc->lvSingleDef = varDsc->lvIsParam;
+ varDsc->lvSingleDefRegCandidate = varDsc->lvIsParam;
}
}
@@ -7405,11 +7407,6 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r
printf(" HFA(%s) ", varTypeName(varDsc->GetHfaType()));
}
- if (varDsc->lvLiveInOutOfHndlr)
- {
- printf(" EH");
- }
-
if (varDsc->lvDoNotEnregister)
{
printf(" do-not-enreg[");
@@ -7427,7 +7424,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r
}
if (lvaEnregEHVars && varDsc->lvLiveInOutOfHndlr)
{
- printf("%c", varDsc->lvDisqualifyEHVarReason);
+ printf("%c", varDsc->lvSingleDefDisqualifyReason);
}
if (varDsc->lvLclFieldExpr)
{
@@ -7500,6 +7497,15 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r
{
printf(" EH-live");
}
+ if (varDsc->lvSpillAtSingleDef)
+ {
+ printf(" spill-single-def");
+ }
+ else if (varDsc->lvSingleDefRegCandidate)
+ {
+ printf(" single-def");
+ }
+
#ifndef TARGET_64BIT
if (varDsc->lvStructDoubleAlign)
printf(" double-align");
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 d2a0191648e18..9499ac5d81782 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -3160,7 +3160,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore)
#endif // !WINDOWS_AMD64_ABI
convertToStoreObj = false;
}
- else if (!varDsc->IsEnregisterable())
+ else if (!varDsc->IsEnregisterableType())
{
convertToStoreObj = true;
}
@@ -3418,11 +3418,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
lclNum = fieldLclNum;
varDsc = comp->lvaGetDesc(lclNum);
}
- else if (!varDsc->lvRegStruct && !varTypeIsEnregisterable(varDsc))
-
+ else if (varDsc->lvPromoted)
{
- // TODO-1stClassStructs: We can no longer promote or enregister this struct,
- // since it is referenced as a whole.
+ // TODO-1stClassStructs: We can no longer independently promote
+ // or enregister this struct, since it is referenced as a whole.
comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_BlockOp));
}
@@ -3434,9 +3433,10 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
}
else
{
- var_types lclVarType = varDsc->GetRegisterType(lclVar);
+ const var_types lclVarType = varDsc->GetRegisterType(lclVar);
assert(lclVarType != TYP_UNDEF);
- lclVar->ChangeType(lclVarType);
+ const var_types actualType = genActualType(lclVarType);
+ lclVar->ChangeType(actualType);
if (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(lclVarType))
{
@@ -4039,12 +4039,12 @@ void Lowering::InsertPInvokeMethodProlog()
GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, argList);
// some sanity checks on the frame list root vardsc
- LclVarDsc* varDsc = &comp->lvaTable[comp->info.compLvFrameListRoot];
+ const unsigned lclNum = comp->info.compLvFrameListRoot;
+ const LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
noway_assert(!varDsc->lvIsParam);
noway_assert(varDsc->lvType == TYP_I_IMPL);
- GenTree* store =
- new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, comp->info.compLvFrameListRoot);
+ GenTree* store = new (comp, GT_STORE_LCL_VAR) GenTreeLclVar(GT_STORE_LCL_VAR, TYP_I_IMPL, lclNum);
store->AsOp()->gtOp1 = call;
store->gtFlags |= GTF_VAR_DEF;
@@ -4065,6 +4065,7 @@ void Lowering::InsertPInvokeMethodProlog()
GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP);
storeSP->gtOp1 = PhysReg(REG_SPBASE);
storeSP->gtFlags |= GTF_VAR_DEF;
+ comp->lvaSetVarDoNotEnregister(comp->lvaInlinedPInvokeFrameVar DEBUGARG(Compiler::DNER_LocalField));
firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeSP));
DISPTREERANGE(firstBlockRange, storeSP);
@@ -6534,7 +6535,7 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret)
assert(varDsc->lvIsMultiRegRet || (varDsc->lvIsHfa() && varTypeIsValidHfaType(varDsc->lvType)));
// Mark var as contained if not enregisterable.
- if (!varTypeIsEnregisterable(op1))
+ if (!varDsc->IsEnregisterableLcl())
{
if (!op1->IsMultiRegLclVar())
{
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 55bfab94f6f5f..ed889f7f383bc 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -269,6 +269,12 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
// address, not knowing that GT_IND is part of a block op that has containment restrictions.
src->AsIndir()->Addr()->ClearContained();
}
+ else if (src->OperIs(GT_LCL_VAR))
+ {
+ // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register.
+ const unsigned srcLclNum = src->AsLclVar()->GetLclNum();
+ comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(Compiler::DNER_BlockOp));
+ }
if (blkNode->OperIs(GT_STORE_OBJ))
{
diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index ddc5442517466..4bbb877fda8c3 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -196,9 +196,9 @@ BasicBlock::weight_t LinearScan::getWeight(RefPosition* refPos)
if (refPos->getInterval()->isSpilled)
{
// Decrease the weight if the interval has already been spilled.
- if (varDsc->lvLiveInOutOfHndlr)
+ if (varDsc->lvLiveInOutOfHndlr || refPos->getInterval()->firstRefPosition->singleDefSpill)
{
- // An EH var is always spilled at defs, and we'll decrease the weight by half,
+ // An EH-var/single-def is always spilled at defs, and we'll decrease the weight by half,
// since only the reload is needed.
weight = weight / 2;
}
@@ -1490,7 +1490,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
// or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning")
// references when using the general GC encoding.
unsigned lclNum = (unsigned)(varDsc - compiler->lvaTable);
- if (varDsc->lvAddrExposed || !varDsc->IsEnregisterable() ||
+ if (varDsc->lvAddrExposed || !varDsc->IsEnregisterableType() ||
(!compiler->compEnregStructLocals() && (varDsc->lvType == TYP_STRUCT)))
{
#ifdef DEBUG
@@ -1794,7 +1794,7 @@ void LinearScan::identifyCandidates()
if (varDsc->lvLiveInOutOfHndlr)
{
- newInt->isWriteThru = varDsc->lvEhWriteThruCandidate;
+ newInt->isWriteThru = varDsc->lvSingleDefRegCandidate;
setIntervalAsSpilled(newInt);
}
@@ -3273,6 +3273,24 @@ void LinearScan::spillInterval(Interval* interval, RefPosition* fromRefPosition
fromRefPosition->spillAfter = true;
}
}
+
+ // Only handle the singledef intervals whose firstRefPosition is RefTypeDef and is not yet marked as spillAfter.
+ // The singledef intervals whose firstRefPositions are already marked as spillAfter, no need to mark them as
+ // singleDefSpill because they will always get spilled at firstRefPosition.
+ // This helps in spilling the singleDef at definition
+ //
+ // Note: Only mark "singleDefSpill" for those intervals who ever get spilled. The intervals that are never spilled
+ // will not be marked as "singleDefSpill" and hence won't get spilled at the first definition.
+ if (interval->isSingleDef && RefTypeIsDef(interval->firstRefPosition->refType) &&
+ !interval->firstRefPosition->spillAfter)
+ {
+ // TODO-CQ: Check if it is beneficial to spill at def, meaning, if it is a hot block don't worry about
+ // doing the spill. Another option is to track number of refpositions and a interval has more than X
+ // refpositions
+ // then perform this optimization.
+ interval->firstRefPosition->singleDefSpill = true;
+ }
+
assert(toRefPosition != nullptr);
#ifdef DEBUG
@@ -3955,16 +3973,16 @@ void LinearScan::unassignIntervalBlockStart(RegRecord* regRecord, VarToRegMap in
//
// Arguments:
// currentBlock - the BasicBlock we are about to allocate registers for
-// allocationPass - true if we are currently allocating registers (versus writing them back)
//
// Return Value:
// None
//
// Notes:
-// During the allocation pass, we use the outVarToRegMap of the selected predecessor to
-// determine the lclVar locations for the inVarToRegMap.
-// During the resolution (write-back) pass, we only modify the inVarToRegMap in cases where
-// a lclVar was spilled after the block had been completed.
+// During the allocation pass (allocationPassComplete = false), we use the outVarToRegMap
+// of the selected predecessor to determine the lclVar locations for the inVarToRegMap.
+// During the resolution (write-back when allocationPassComplete = true) pass, we only
+// modify the inVarToRegMap in cases where a lclVar was spilled after the block had been
+// completed.
void LinearScan::processBlockStartLocations(BasicBlock* currentBlock)
{
// If we have no register candidates we should only call this method during allocation.
@@ -5915,7 +5933,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref
assert(currentRefPosition->refType == RefTypeExpUse);
}
}
- else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) &&
+ else if (spillAfter && !RefTypeIsUse(currentRefPosition->refType) && (treeNode != nullptr) &&
(!treeNode->IsMultiReg() || treeNode->gtGetOp1()->IsMultiRegNode()))
{
// In the case of a pure def, don't bother spilling - just assign it to the
@@ -5926,10 +5944,7 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref
assert(interval->isSpilled);
varDsc->SetRegNum(REG_STK);
interval->physReg = REG_NA;
- if (treeNode != nullptr)
- {
- writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA);
- }
+ writeLocalReg(treeNode->AsLclVar(), interval->varNum, REG_NA);
}
else // Not reload and Not pure-def that's spillAfter
{
@@ -6018,6 +6033,27 @@ void LinearScan::resolveLocalRef(BasicBlock* block, GenTreeLclVar* treeNode, Ref
}
}
}
+
+ if (currentRefPosition->singleDefSpill && (treeNode != nullptr))
+ {
+ // This is the first (and only) def of a single-def var (only defs are marked 'singleDefSpill').
+ // Mark it as GTF_SPILL, so it is spilled immediately to the stack at definition and
+ // GTF_SPILLED, so the variable stays live in the register.
+ //
+ // TODO: This approach would still create the resolution moves but during codegen, will check for
+ // `lvSpillAtSingleDef` to decide whether to generate spill or not. In future, see if there is some
+ // better way to avoid resolution moves, perhaps by updating the varDsc->SetRegNum(REG_STK) in this
+ // method?
+ treeNode->gtFlags |= GTF_SPILL;
+ treeNode->gtFlags |= GTF_SPILLED;
+
+ if (treeNode->IsMultiReg())
+ {
+ treeNode->SetRegSpillFlagByIdx(GTF_SPILLED, currentRefPosition->getMultiRegIdx());
+ }
+
+ varDsc->lvSpillAtSingleDef = true;
+ }
}
// Update the physRegRecord for the register, so that we know what vars are in
@@ -8936,6 +8972,10 @@ void RefPosition::dump(LinearScan* linearScan)
{
printf(" spillAfter");
}
+ if (this->singleDefSpill)
+ {
+ printf(" singleDefSpill");
+ }
if (this->writeThru)
{
printf(" writeThru");
@@ -9678,12 +9718,12 @@ void LinearScan::dumpLsraAllocationEvent(
case LSRA_EVENT_DONE_KILL_GC_REFS:
dumpRefPositionShort(activeRefPosition, currentBlock);
- printf("Done ");
+ printf("Done ");
break;
case LSRA_EVENT_NO_GC_KILLS:
dumpRefPositionShort(activeRefPosition, currentBlock);
- printf("None ");
+ printf("None ");
break;
// Block boundaries
diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h
index 10ff1f471b4a7..b0c1735871695 100644
--- a/src/coreclr/jit/lsra.h
+++ b/src/coreclr/jit/lsra.h
@@ -1928,6 +1928,7 @@ class Interval : public Referenceable
, isPartiallySpilled(false)
#endif
, isWriteThru(false)
+ , isSingleDef(false)
#ifdef DEBUG
, intervalIndex(0)
#endif
@@ -2023,6 +2024,9 @@ class Interval : public Referenceable
// True if this interval is associated with a lclVar that is written to memory at each definition.
bool isWriteThru : 1;
+ // True if this interval has a single definition.
+ bool isSingleDef : 1;
+
#ifdef DEBUG
unsigned int intervalIndex;
#endif // DEBUG
@@ -2222,6 +2226,10 @@ class RefPosition
// Spill and Copy info
// reload indicates that the value was spilled, and must be reloaded here.
// spillAfter indicates that the value is spilled here, so a spill must be added.
+ // singleDefSpill indicates that it is associated with a single-def var and if it
+ // is decided to get spilled, it will be spilled at firstRefPosition def. That
+ // way, the the value of stack will always be up-to-date and no more spills or
+ // resolutions (from reg to stack) will be needed for such single-def var.
// copyReg indicates that the value needs to be copied to a specific register,
// but that it will also retain its current assigned register.
// moveReg indicates that the value needs to be moved to a different register,
@@ -2240,6 +2248,7 @@ class RefPosition
unsigned char reload : 1;
unsigned char spillAfter : 1;
+ unsigned char singleDefSpill : 1;
unsigned char writeThru : 1; // true if this var is defined in a register and also spilled. spillAfter must NOT be
// set.
@@ -2287,6 +2296,7 @@ class RefPosition
, lastUse(false)
, reload(false)
, spillAfter(false)
+ , singleDefSpill(false)
, writeThru(false)
, copyReg(false)
, moveReg(false)
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index cb04ddddf51c0..f5f4d781d759e 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -620,6 +620,12 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval,
associateRefPosWithInterval(newRP);
+ if (RefTypeIsDef(newRP->refType))
+ {
+ assert(theInterval != nullptr);
+ theInterval->isSingleDef = theInterval->firstRefPosition == newRP;
+ }
+
DBEXEC(VERBOSE, newRP->dump(this));
return newRP;
}
@@ -2602,20 +2608,20 @@ void LinearScan::buildIntervals()
{
lsraDumpIntervals("BEFORE VALIDATING INTERVALS");
dumpRefPositions("BEFORE VALIDATING INTERVALS");
- validateIntervals();
}
+ validateIntervals();
+
#endif // DEBUG
}
#ifdef DEBUG
//------------------------------------------------------------------------
-// validateIntervals: A DEBUG-only method that checks that the lclVar RefPositions
-// do not reflect uses of undefined values
-//
-// Notes: If an undefined use is encountered, it merely prints a message.
+// validateIntervals: A DEBUG-only method that checks that:
+// - the lclVar RefPositions do not reflect uses of undefined values
+// - A singleDef interval should have just first RefPosition as RefTypeDef.
//
-// TODO-Cleanup: This should probably assert, or at least print the message only
-// when doing a JITDUMP.
+// TODO-Cleanup: If an undefined use is encountered, it merely prints a message
+// but probably assert.
//
void LinearScan::validateIntervals()
{
@@ -2630,19 +2636,29 @@ void LinearScan::validateIntervals()
Interval* interval = getIntervalForLocalVar(i);
bool defined = false;
- printf("-----------------\n");
+ JITDUMP("-----------------\n");
for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition)
{
- ref->dump(this);
+ if (VERBOSE)
+ {
+ ref->dump(this);
+ }
RefType refType = ref->refType;
if (!defined && RefTypeIsUse(refType))
{
if (compiler->info.compMethodName != nullptr)
{
- printf("%s: ", compiler->info.compMethodName);
+ JITDUMP("%s: ", compiler->info.compMethodName);
}
- printf("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation);
+ JITDUMP("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation);
+ }
+
+ // For single-def intervals, the only the first refposition should be a RefTypeDef
+ if (interval->isSingleDef && RefTypeIsDef(refType))
+ {
+ assert(ref == interval->firstRefPosition);
}
+
// Note that there can be multiple last uses if they are on disjoint paths,
// so we can't really check the lastUse flag
if (ref->lastUse)
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 2b40d8548c68f..68f31af763634 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -74,6 +74,9 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall:
call->gtCallMoreFlags = GTF_CALL_M_EMPTY;
call->gtInlineCandidateInfo = nullptr;
call->gtControlExpr = nullptr;
+#ifdef UNIX_X86_ABI
+ call->gtFlags |= GTF_CALL_POP_ARGS;
+#endif // UNIX_X86_ABI
#if DEBUG
// Helper calls are never candidates.
@@ -639,7 +642,6 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
case GT_IND:
case GT_CLS_VAR:
case GT_LCL_FLD:
- case GT_ARR_ELEM:
oper->gtType = dstType;
// We're changing the type here so we need to update the VN;
// in other cases we discard the cast without modifying oper
@@ -2895,9 +2897,10 @@ void Compiler::fgInitArgInfo(GenTreeCall* call)
}
#ifdef TARGET_X86
- // Compute the maximum number of arguments that can be passed in registers.
- // For X86 we handle the varargs and unmanaged calling conventions
+// Compute the maximum number of arguments that can be passed in registers.
+// For X86 we handle the varargs and unmanaged calling conventions
+#ifndef UNIX_X86_ABI
if (call->gtFlags & GTF_CALL_POP_ARGS)
{
noway_assert(intArgRegNum < MAX_REG_ARG);
@@ -2908,6 +2911,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call)
if (callHasRetBuffArg)
maxRegArgs++;
}
+#endif // UNIX_X86_ABI
if (call->IsUnmanaged())
{
@@ -5577,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
@@ -5738,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;
@@ -5748,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;
}
@@ -17437,14 +17442,18 @@ void Compiler::fgRetypeImplicitByRefArgs()
#endif // DEBUG
// Propagate address-taken-ness and do-not-enregister-ness.
- newVarDsc->lvAddrExposed = varDsc->lvAddrExposed;
- newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister;
+ newVarDsc->lvAddrExposed = varDsc->lvAddrExposed;
+ newVarDsc->lvDoNotEnregister = varDsc->lvDoNotEnregister;
+ newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr;
+ newVarDsc->lvSingleDef = varDsc->lvSingleDef;
+ newVarDsc->lvSingleDefRegCandidate = varDsc->lvSingleDefRegCandidate;
+ newVarDsc->lvSpillAtSingleDef = varDsc->lvSpillAtSingleDef;
#ifdef DEBUG
- newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr;
- newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr;
- newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr;
- newVarDsc->lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr;
- newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall;
+ newVarDsc->lvLclBlockOpAddr = varDsc->lvLclBlockOpAddr;
+ newVarDsc->lvLclFieldExpr = varDsc->lvLclFieldExpr;
+ newVarDsc->lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr;
+ newVarDsc->lvSingleDefDisqualifyReason = varDsc->lvSingleDefDisqualifyReason;
+ newVarDsc->lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall;
#endif // DEBUG
// If the promotion is dependent, the promoted temp would just be committed
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/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp
index 5c95b91883c52..a37f11cedaeee 100644
--- a/src/coreclr/jit/rangecheck.cpp
+++ b/src/coreclr/jit/rangecheck.cpp
@@ -865,16 +865,16 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE
if (pred->bbFallsThrough() && pred->bbNext == block)
{
assertions = pred->bbAssertionOut;
- JITDUMP("Merge assertions from pred " FMT_BB " edge: %s\n", pred->bbNum,
- BitVecOps::ToString(m_pCompiler->apTraits, assertions));
+ JITDUMP("Merge assertions from pred " FMT_BB " edge: ", pred->bbNum);
+ Compiler::optDumpAssertionIndices(assertions, "\n");
}
else if ((pred->bbJumpKind == BBJ_COND || pred->bbJumpKind == BBJ_ALWAYS) && pred->bbJumpDest == block)
{
if (m_pCompiler->bbJtrueAssertionOut != nullptr)
{
assertions = m_pCompiler->bbJtrueAssertionOut[pred->bbNum];
- JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: %s\n", pred->bbNum,
- BitVecOps::ToString(m_pCompiler->apTraits, assertions));
+ JITDUMP("Merge assertions from pred " FMT_BB " JTrue edge: ", pred->bbNum);
+ Compiler::optDumpAssertionIndices(assertions, "\n");
}
}
}
@@ -1012,9 +1012,10 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block,
Range range = GetRange(ssaDef->GetBlock(), ssaDef->GetAssignment()->gtGetOp2(), monIncreasing DEBUGARG(indent));
if (!BitVecOps::MayBeUninit(block->bbAssertionIn) && (m_pCompiler->GetAssertionCount() > 0))
{
- JITDUMP("Merge assertions from " FMT_BB ":%s for assignment about [%06d]\n", block->bbNum,
- BitVecOps::ToString(m_pCompiler->apTraits, block->bbAssertionIn),
- Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1()));
+ JITDUMP("Merge assertions from " FMT_BB ": ", block->bbNum);
+ Compiler::optDumpAssertionIndices(block->bbAssertionIn, " ");
+ JITDUMP("for assignment about [%06d]\n", Compiler::dspTreeID(ssaDef->GetAssignment()->gtGetOp1()))
+
MergeEdgeAssertions(ssaDef->GetAssignment()->gtGetOp1()->AsLclVarCommon(), block->bbAssertionIn, &range);
JITDUMP("done merging\n");
}
diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp
index 20a9745362b57..15c32596cc422 100644
--- a/src/coreclr/jit/treelifeupdater.cpp
+++ b/src/coreclr/jit/treelifeupdater.cpp
@@ -60,7 +60,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns
{
regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex);
bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA;
- isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr;
+ isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory();
if (isInReg)
{
if (isBorn)
@@ -259,7 +259,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree)
compiler->codeGen->genUpdateVarReg(varDsc, tree);
}
bool isInReg = varDsc->lvIsInReg() && tree->GetRegNum() != REG_NA;
- bool isInMemory = !isInReg || varDsc->lvLiveInOutOfHndlr;
+ bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory();
if (isInReg)
{
compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree));
@@ -283,7 +283,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree)
unsigned fldVarIndex = fldVarDsc->lvVarIndex;
regNumber reg = lclVarTree->AsLclVar()->GetRegNumByIdx(i);
bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA;
- bool isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr;
+ bool isInMemory = !isInReg || fldVarDsc->IsAlwaysAliveInMemory();
bool isFieldDying = lclVarTree->AsLclVar()->IsLastUse(i);
if ((isBorn && !isFieldDying) || (!isBorn && isFieldDying))
{
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..a50b326861aad
--- /dev/null
+++ b/src/coreclr/minipal/Unix/doublemapping.cpp
@@ -0,0 +1,211 @@
+// 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
+#ifdef TARGET_LINUX
+#include
+#include // __NR_memfd_create
+#endif // TARGET_LINUX
+#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
+
+#ifdef TARGET_LINUX
+#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__)
+#endif // TARGET_LINUX
+
+#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/scripts/genEventPipe.py b/src/coreclr/scripts/genEventPipe.py
index 380710d4a009b..1076b181e316d 100644
--- a/src/coreclr/scripts/genEventPipe.py
+++ b/src/coreclr/scripts/genEventPipe.py
@@ -72,11 +72,19 @@ def generateMethodSignatureWrite(eventName, template, extern, runtimeFlavor):
sig_pieces.append(")")
return ''.join(sig_pieces)
+def includeProvider(providerName, runtimeFlavor):
+ if runtimeFlavor.coreclr and providerName == "Microsoft-DotNETRuntimeMonoProfiler":
+ return False
+ else:
+ return True
+
def includeEvent(inclusionList, providerName, eventName):
if len(inclusionList) == 0:
return True
if providerName in inclusionList and eventName in inclusionList[providerName]:
return True
+ elif providerName in inclusionList and "*" in inclusionList[providerName]:
+ return True
elif "*" in inclusionList and eventName in inclusionList["*"]:
return True
elif "*" in inclusionList and "*" in inclusionList["*"]:
@@ -340,6 +348,10 @@ def generateWriteEventBody(template, providerName, eventName, runtimeFlavor):
pack_list.append(
" success &= write_buffer_double_t(%s, &buffer, &offset, &size, &fixedBuffer);" %
(parameter.name,))
+ elif parameter.winType == "win:Pointer" and runtimeFlavor.mono:
+ pack_list.append(
+ " success &= write_buffer_uintptr_t((uintptr_t)%s, &buffer, &offset, &size, &fixedBuffer);" %
+ (parameter.name,))
elif runtimeFlavor.mono:
pack_list.append(
" success &= write_buffer((const uint8_t *)%s, sizeof(%s), &buffer, &offset, &size, &fixedBuffer);" %
@@ -669,16 +681,17 @@ def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, target_cpp, ru
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
- providerPrettyName = providerName.replace("Windows-", '')
- providerPrettyName = providerPrettyName.replace("Microsoft-", '')
- providerPrettyName = providerPrettyName.replace('-', '_')
- if extern: helper.write(
- 'extern "C" '
- )
- helper.write(
- "void Init" +
- providerPrettyName +
- "(void);\n\n")
+ if includeProvider(providerName, runtimeFlavor):
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ if extern: helper.write(
+ 'extern "C" '
+ )
+ helper.write(
+ "void Init" +
+ providerPrettyName +
+ "(void);\n\n")
if extern: helper.write(
'extern "C" '
@@ -687,10 +700,11 @@ def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, target_cpp, ru
helper.write("void InitProvidersAndEvents(void)\n{\n")
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
- providerPrettyName = providerName.replace("Windows-", '')
- providerPrettyName = providerPrettyName.replace("Microsoft-", '')
- providerPrettyName = providerPrettyName.replace('-', '_')
- helper.write(" Init" + providerPrettyName + "();\n")
+ if includeProvider(providerName, runtimeFlavor):
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ helper.write(" Init" + providerPrettyName + "();\n")
helper.write("}\n")
if runtimeFlavor.coreclr:
@@ -892,6 +906,19 @@ def getMonoEventPipeImplFilePrefix():
return write_buffer_int32_t (value, buffer, offset, size, fixed_buffer);
}
+static
+inline
+bool
+write_buffer_uintptr_t (
+ uintptr_t value,
+ uint8_t **buffer,
+ size_t *offset,
+ size_t *size,
+ bool *fixed_buffer)
+{
+ return write_buffer ((const uint8_t *)&value, sizeof (uintptr_t), buffer, offset, size, fixed_buffer);
+}
+
static
inline
EventPipeEvent *
@@ -949,6 +976,8 @@ def generateEventPipeImplFiles(
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
+ if not includeProvider(providerName, runtimeFlavor):
+ continue
providerPrettyName = providerName.replace("Windows-", '')
providerPrettyName = providerPrettyName.replace("Microsoft-", '')
diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs
index 00ee6f8712136..32b42394b6afe 100644
--- a/src/coreclr/tools/dotnet-pgo/Program.cs
+++ b/src/coreclr/tools/dotnet-pgo/Program.cs
@@ -577,7 +577,6 @@ void PrintBar(string label, ref int curIndex, Func include, bool f
PrintBar($">{(int)proportion,2}%", ref curIndex, d => d * 100 > proportion, true);
PrintBar("0%", ref curIndex, d => true, false);
- var wtf = sorted.Where(t => double.IsNaN(t.Overlap)).ToList();
PrintOutput(FormattableString.Invariant($"The average overlap is {sorted.Average(t => t.Overlap)*100:F2}% for the {sorted.Count} methods with matching flow graphs and profile data"));
double mse = sorted.Sum(t => (100 - t.Overlap*100) * (100 - t.Overlap*100)) / sorted.Count;
PrintOutput(FormattableString.Invariant($"The mean squared error is {mse:F2}"));
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/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 1d682d2a428bb..9c2cb3df0b7e9 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -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 d8a275c6da629..45895f16fce48 100644
--- a/src/coreclr/vm/ClrEtwAll.man
+++ b/src/coreclr/vm/ClrEtwAll.man
@@ -7052,6 +7052,930 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %3
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+ %6
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+ %1
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+ %6
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+ %1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+ %6
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -8044,6 +8968,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/coreclr/vm/ClrEtwAllMeta.lst b/src/coreclr/vm/ClrEtwAllMeta.lst
index 285e9101c6321..4ac4fe405d9da 100644
--- a/src/coreclr/vm/ClrEtwAllMeta.lst
+++ b/src/coreclr/vm/ClrEtwAllMeta.lst
@@ -633,3 +633,33 @@ nomac:StressLogTask:::StressLogEvent_V1
# StackWalk events
##################
nomac:CLRStackStress:::CLRStackWalkStress
+
+#################################
+# Events from the Mono profiler provider
+#################################
+nostack::MonoProfiler::ExceptionClause
+nostack::MonoProfiler::MonoProfilerMethodEnter
+nostack::MonoProfiler::MonoProfilerMethodLeave
+nostack::MonoProfiler::MonoProfilerMethodTailCall
+nostack::MonoProfiler::MonoProfilerMethodExceptionLeave
+nostack::MonoProfiler::MonoProfilerMethodFree
+nostack::MonoProfiler::MonoProfilerMethodBeginInvoke
+nostack::MonoProfiler::MonoProfilerMethodEndInvoke
+nostack::MonoProfiler::MonoProfilerGCEvent
+nostack::MonoProfiler::MonoProfilerGCMoves
+nostack::MonoProfiler::MonoProfilerGCResize
+nostack::MonoProfiler::MonoProfilerGCFinalizing
+nostack::MonoProfiler::MonoProfilerGCFinalized
+nostack::MonoProfiler::MonoProfilerGCFinalizingObject
+nostack::MonoProfiler::MonoProfilerGCFinalizedObject
+nostack::MonoProfiler::MonoProfilerGCRootRegister
+nostack::MonoProfiler::MonoProfilerGCRootUnregister
+nostack::MonoProfiler::MonoProfilerGCRoots
+nostack::MonoProfiler::MonoProfilerGCHeapDumpStart
+nostack::MonoProfiler::MonoProfilerGCHeapDumpStop
+nostack::MonoProfiler::MonoProfilerGCHeapDumpObjectReference
+nostack::MonoProfiler::MonoProfilerThreadStarted
+nostack::MonoProfiler::MonoProfilerThreadStopping
+nostack::MonoProfiler::MonoProfilerThreadStopped
+nostack::MonoProfiler::MonoProfilerThreadExited
+nostack::MonoProfiler::MonoProfilerThreadName
\ No newline at end of file
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..1b61e16dec5d3 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);
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/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/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/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/gcenv.os.cpp b/src/coreclr/vm/gcenv.os.cpp
index de803cca4310d..b585df950948d 100644
--- a/src/coreclr/vm/gcenv.os.cpp
+++ b/src/coreclr/vm/gcenv.os.cpp
@@ -892,6 +892,13 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
MEMORYSTATUSEX memStatus;
GetProcessMemoryLoad(&memStatus);
+#ifndef TARGET_UNIX
+ // For 32-bit processes the virtual address range could be smaller than the amount of physical
+ // memory on the machine/in the container, we need to restrict by the VM.
+ if (memStatus.ullTotalVirtual < memStatus.ullTotalPhys)
+ return memStatus.ullTotalVirtual;
+#endif
+
return memStatus.ullTotalPhys;
}
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 cefe7ecadc5e9..0467f347aaacb 100644
--- a/src/coreclr/vm/i386/jitinterfacex86.cpp
+++ b/src/coreclr/vm/i386/jitinterfacex86.cpp
@@ -490,6 +490,12 @@ void *JIT_TrialAlloc::GenBox(Flags flags)
// Do call to CopyValueClassUnchecked(object, data, pMT)
+#ifdef UNIX_X86_ABI
+#define STACK_ALIGN_PADDING 12
+ // Make pad to align esp
+ sl.X86EmitSubEsp(STACK_ALIGN_PADDING);
+#endif // UNIX_X86_ABI
+
// Pass pMT (still in ECX)
sl.X86EmitPushReg(kECX);
@@ -507,6 +513,11 @@ void *JIT_TrialAlloc::GenBox(Flags flags)
// call CopyValueClass
sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID) CopyValueClassUnchecked), 12);
+#ifdef UNIX_X86_ABI
+ // Make pad to align esp
+ sl.X86EmitAddEsp(STACK_ALIGN_PADDING);
+#undef STACK_ALIGN_PADDING
+#endif // UNIX_X86_ABI
// Restore the address of the newly allocated object and return it.
// mov eax,ebx
@@ -1039,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);
@@ -1058,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
}
@@ -1121,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]);
@@ -1159,7 +1178,7 @@ void ValidateWriteBarrierHelpers()
#endif //CODECOVERAGE
/*********************************************************************/
-#define WriteBarrierIsPreGrow() (((BYTE *)JIT_WriteBarrierEAX)[10] == 0xc1)
+#define WriteBarrierIsPreGrow() ((GetWriteBarrierCodeLocation((BYTE *)JIT_WriteBarrierEAX))[10] == 0xc1)
/*********************************************************************/
@@ -1177,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)
{
@@ -1203,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)
{
@@ -1233,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
@@ -1242,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.
@@ -1260,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);
@@ -1276,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
@@ -1313,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
@@ -1329,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/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..2c55f8770b01b 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.
@@ -1138,50 +1157,80 @@ void InitThreadManager()
_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);
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/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/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props
index 4eea2cf850fa0..28ac4374be2ae 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props
@@ -70,6 +70,18 @@
runtimes/$(RuntimeIdentifier)/native/include/%(RecursiveDir)
+
+ runtimes/$(RuntimeIdentifier)/native/Mono.release.framework/%(RecursiveDir)
+
+
+
+ runtimes/$(RuntimeIdentifier)/native/Mono.debug.framework/%(RecursiveDir)
+
+
diff --git a/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs
new file mode 100644
index 0000000000000..d9807b427bf26
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/Interop.DefaultPathBufferSize.cs
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+internal static partial class Interop
+{
+ // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths
+ // without putting too much pressure on the stack.
+ internal const int DefaultPathBufferSize = 256;
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs
index 1faef8cc0be8d..78da5a667310f 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs
@@ -14,18 +14,16 @@ internal static partial class Sys
internal static unsafe string GetCwd()
{
- const int StackLimit = 256;
-
// First try to get the path into a buffer on the stack
- byte* stackBuf = stackalloc byte[StackLimit];
- string? result = GetCwdHelper(stackBuf, StackLimit);
+ byte* stackBuf = stackalloc byte[DefaultPathBufferSize];
+ string? result = GetCwdHelper(stackBuf, DefaultPathBufferSize);
if (result != null)
{
return result;
}
// If that was too small, try increasing large buffer sizes
- int bufferSize = StackLimit;
+ int bufferSize = DefaultPathBufferSize;
while (true)
{
checked { bufferSize *= 2; }
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
index fb59b17534680..05d74c84e789d 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
@@ -22,129 +22,6 @@ static Sys()
private const int MountPointFormatBufferSizeInBytes = 32;
- ///
- /// Internal FileSystem names and magic numbers taken from man(2) statfs
- ///
- ///
- /// These value names MUST be kept in sync with those in GetDriveType below,
- /// where this enum must be a subset of the GetDriveType list, with the enum
- /// values here exactly matching a string there.
- ///
- internal enum UnixFileSystemTypes : long
- {
- adfs = 0xADF5,
- affs = 0xADFF,
- afs = 0x5346414F,
- anoninode = 0x09041934,
- aufs = 0x61756673,
- autofs = 0x0187,
- autofs4 = 0x6D4A556D,
- befs = 0x42465331,
- bdevfs = 0x62646576,
- bfs = 0x1BADFACE,
- binfmt_misc = 0x42494E4D,
- bootfs = 0xA56D3FF9,
- btrfs = 0x9123683E,
- ceph = 0x00C36400,
- cgroupfs = 0x0027E0EB,
- cgroup2fs = 0x63677270,
- cifs = 0xFF534D42,
- coda = 0x73757245,
- coherent = 0x012FF7B7,
- configfs = 0x62656570,
- cramfs = 0x28CD3D45,
- debugfs = 0x64626720,
- devfs = 0x1373,
- devpts = 0x1CD1,
- ecryptfs = 0xF15F,
- efs = 0x00414A53,
- exofs = 0x5DF5,
- ext = 0x137D,
- ext2_old = 0xEF51,
- ext2 = 0xEF53,
- ext3 = 0xEF53,
- ext4 = 0xEF53,
- fat = 0x4006,
- fd = 0xF00D1E,
- fhgfs = 0x19830326,
- fuse = 0x65735546,
- fuseblk = 0x65735546,
- fusectl = 0x65735543,
- futexfs = 0x0BAD1DEA,
- gfsgfs2 = 0x1161970,
- gfs2 = 0x01161970,
- gpfs = 0x47504653,
- hfs = 0x4244,
- hfsplus = 0x482B,
- hpfs = 0xF995E849,
- hugetlbfs = 0x958458F6,
- inodefs = 0x11307854,
- inotifyfs = 0x2BAD1DEA,
- isofs = 0x9660,
- // isofs = 0x4004, // R_WIN
- // isofs = 0x4000, // WIN
- jffs = 0x07C0,
- jffs2 = 0x72B6,
- jfs = 0x3153464A,
- kafs = 0x6B414653,
- lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */
- logfs = 0xC97E8168,
- lustre = 0x0BD00BD0,
- minix_old = 0x137F, /* orig. minix */
- minix = 0x138F, /* 30 char minix */
- minix2 = 0x2468, /* minix V2 */
- minix2v2 = 0x2478, /* MINIX V2, 30 char names */
- minix3 = 0x4D5A,
- mqueue = 0x19800202,
- msdos = 0x4D44,
- nfs = 0x6969,
- nfsd = 0x6E667364,
- nilfs = 0x3434,
- novell = 0x564C,
- ntfs = 0x5346544E,
- openprom = 0x9FA1,
- ocfs2 = 0x7461636F,
- omfs = 0xC2993D87,
- overlay = 0x794C7630,
- overlayfs = 0x794C764F,
- panfs = 0xAAD7AAEA,
- pipefs = 0x50495045,
- proc = 0x9FA0,
- pstorefs = 0x6165676C,
- qnx4 = 0x002F,
- qnx6 = 0x68191122,
- ramfs = 0x858458F6,
- reiserfs = 0x52654973,
- romfs = 0x7275,
- rootfs = 0x53464846,
- rpc_pipefs = 0x67596969,
- samba = 0x517B,
- securityfs = 0x73636673,
- selinux = 0xF97CFF8C,
- smb = 0x517B,
- sockfs = 0x534F434B,
- squashfs = 0x73717368,
- sysfs = 0x62656572,
- sysv2 = 0x012FF7B6,
- sysv4 = 0x012FF7B5,
- tmpfs = 0x01021994,
- ubifs = 0x24051905,
- udf = 0x15013346,
- ufs = 0x00011954,
- ufscigam = 0x54190100, // ufs byteswapped
- ufs2 = 0x19540119,
- usbdevice = 0x9FA2,
- v9fs = 0x01021997,
- vmhgfs = 0xBACBACBC,
- vxfs = 0xA501FCF5,
- vzfs = 0x565A4653,
- xenfs = 0xABBA1974,
- xenix = 0x012FF7B4,
- xfs = 0x58465342,
- xia = 0x012FD16D,
- zfs = 0x2FC12FC1,
- }
-
[StructLayout(LayoutKind.Sequential)]
internal struct MountPointInformation
{
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs
index 8f0f6a15fed95..94f37d4ccc3f8 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs
@@ -4,6 +4,7 @@
using System.Runtime.InteropServices;
using System.Buffers;
using System.Text;
+using System;
internal static partial class Interop
{
@@ -20,24 +21,31 @@ internal static partial class Sys
/// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error.
///
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)]
- private static extern int ReadLink(string path, byte[] buffer, int bufferSize);
+ private static extern int ReadLink(ref byte path, byte[] buffer, int bufferSize);
///
/// Takes a path to a symbolic link and returns the link target path.
///
- /// The path to the symlink
- ///
- /// Returns the link to the target path on success; and null otherwise.
- ///
- public static string? ReadLink(string path)
+ /// The path to the symlink.
+ /// Returns the link to the target path on success; and null otherwise.
+ internal static string? ReadLink(ReadOnlySpan path)
{
- int bufferSize = 256;
+ int outputBufferSize = 1024;
+
+ // Use an initial buffer size that prevents disposing and renting
+ // a second time when calling ConvertAndTerminateString.
+ using var converter = new ValueUtf8Converter(stackalloc byte[1024]);
+
while (true)
{
- byte[] buffer = ArrayPool.Shared.Rent(bufferSize);
+ byte[] buffer = ArrayPool.Shared.Rent(outputBufferSize);
try
{
- int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length);
+ int resultLength = Interop.Sys.ReadLink(
+ ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)),
+ buffer,
+ buffer.Length);
+
if (resultLength < 0)
{
// error
@@ -54,8 +62,8 @@ internal static partial class Sys
ArrayPool.Shared.Return(buffer);
}
- // buffer was too small, loop around again and try with a larger buffer.
- bufferSize *= 2;
+ // Output buffer was too small, loop around again and try with a larger buffer.
+ outputBufferSize = buffer.Length * 2;
}
}
}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs
index 3c638cb60aa52..85028fd0fd088 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.Span.cs
@@ -9,16 +9,12 @@ internal static partial class Interop
{
internal static partial class Sys
{
- // Unix max paths are typically 1K or 4K UTF-8 bytes, 256 should handle the majority of paths
- // without putting too much pressure on the stack.
- private const int StackBufferSize = 256;
-
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)]
internal static extern int Stat(ref byte path, out FileStatus output);
internal static int Stat(ReadOnlySpan path, out FileStatus output)
{
- var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]);
+ var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]);
int result = Stat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output);
converter.Dispose();
return result;
@@ -29,7 +25,7 @@ internal static int Stat(ReadOnlySpan path, out FileStatus output)
internal static int LStat(ReadOnlySpan path, out FileStatus output)
{
- var converter = new ValueUtf8Converter(stackalloc byte[StackBufferSize]);
+ var converter = new ValueUtf8Converter(stackalloc byte[DefaultPathBufferSize]);
int result = LStat(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), out output);
converter.Dispose();
return result;
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs
new file mode 100644
index 0000000000000..922ecd5bc6625
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SymLink.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SymLink", SetLastError = true)]
+ internal static extern int SymLink(string target, string linkPath);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs
new file mode 100644
index 0000000000000..fd34f87418223
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ ///
+ /// Internal FileSystem names and magic numbers taken from man(2) statfs
+ ///
+ ///
+ /// These value names MUST be kept in sync with those in GetDriveType (moved to Interop.MountPoints.FormatInfo.cs),
+ /// where this enum must be a subset of the GetDriveType list, with the enum
+ /// values here exactly matching a string there.
+ ///
+ internal enum UnixFileSystemTypes : long
+ {
+ adfs = 0xADF5,
+ affs = 0xADFF,
+ afs = 0x5346414F,
+ anoninode = 0x09041934,
+ aufs = 0x61756673,
+ autofs = 0x0187,
+ autofs4 = 0x6D4A556D,
+ befs = 0x42465331,
+ bdevfs = 0x62646576,
+ bfs = 0x1BADFACE,
+ binfmt_misc = 0x42494E4D,
+ bootfs = 0xA56D3FF9,
+ btrfs = 0x9123683E,
+ ceph = 0x00C36400,
+ cgroupfs = 0x0027E0EB,
+ cgroup2fs = 0x63677270,
+ cifs = 0xFF534D42,
+ coda = 0x73757245,
+ coherent = 0x012FF7B7,
+ configfs = 0x62656570,
+ cramfs = 0x28CD3D45,
+ debugfs = 0x64626720,
+ devfs = 0x1373,
+ devpts = 0x1CD1,
+ ecryptfs = 0xF15F,
+ efs = 0x00414A53,
+ exofs = 0x5DF5,
+ ext = 0x137D,
+ ext2_old = 0xEF51,
+ ext2 = 0xEF53,
+ ext3 = 0xEF53,
+ ext4 = 0xEF53,
+ fat = 0x4006,
+ fd = 0xF00D1E,
+ fhgfs = 0x19830326,
+ fuse = 0x65735546,
+ fuseblk = 0x65735546,
+ fusectl = 0x65735543,
+ futexfs = 0x0BAD1DEA,
+ gfsgfs2 = 0x1161970,
+ gfs2 = 0x01161970,
+ gpfs = 0x47504653,
+ hfs = 0x4244,
+ hfsplus = 0x482B,
+ hpfs = 0xF995E849,
+ hugetlbfs = 0x958458F6,
+ inodefs = 0x11307854,
+ inotifyfs = 0x2BAD1DEA,
+ isofs = 0x9660,
+ // isofs = 0x4004, // R_WIN
+ // isofs = 0x4000, // WIN
+ jffs = 0x07C0,
+ jffs2 = 0x72B6,
+ jfs = 0x3153464A,
+ kafs = 0x6B414653,
+ lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */
+ logfs = 0xC97E8168,
+ lustre = 0x0BD00BD0,
+ minix_old = 0x137F, /* orig. minix */
+ minix = 0x138F, /* 30 char minix */
+ minix2 = 0x2468, /* minix V2 */
+ minix2v2 = 0x2478, /* MINIX V2, 30 char names */
+ minix3 = 0x4D5A,
+ mqueue = 0x19800202,
+ msdos = 0x4D44,
+ nfs = 0x6969,
+ nfsd = 0x6E667364,
+ nilfs = 0x3434,
+ novell = 0x564C,
+ ntfs = 0x5346544E,
+ openprom = 0x9FA1,
+ ocfs2 = 0x7461636F,
+ omfs = 0xC2993D87,
+ overlay = 0x794C7630,
+ overlayfs = 0x794C764F,
+ panfs = 0xAAD7AAEA,
+ pipefs = 0x50495045,
+ proc = 0x9FA0,
+ pstorefs = 0x6165676C,
+ qnx4 = 0x002F,
+ qnx6 = 0x68191122,
+ ramfs = 0x858458F6,
+ reiserfs = 0x52654973,
+ romfs = 0x7275,
+ rootfs = 0x53464846,
+ rpc_pipefs = 0x67596969,
+ samba = 0x517B,
+ securityfs = 0x73636673,
+ selinux = 0xF97CFF8C,
+ smb = 0x517B,
+ smb2 = 0xFE534D42,
+ sockfs = 0x534F434B,
+ squashfs = 0x73717368,
+ sysfs = 0x62656572,
+ sysv2 = 0x012FF7B6,
+ sysv4 = 0x012FF7B5,
+ tmpfs = 0x01021994,
+ ubifs = 0x24051905,
+ udf = 0x15013346,
+ ufs = 0x00011954,
+ ufscigam = 0x54190100, // ufs byteswapped
+ ufs2 = 0x19540119,
+ usbdevice = 0x9FA2,
+ v9fs = 0x01021997,
+ vmhgfs = 0xBACBACBC,
+ vxfs = 0xA501FCF5,
+ vzfs = 0x565A4653,
+ xenfs = 0xABBA1974,
+ xenix = 0x012FF7B4,
+ xfs = 0x58465342,
+ xia = 0x012FD16D,
+ zfs = 0x2FC12FC1,
+ }
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetFileSystemType")]
+ private static extern long GetFileSystemType(SafeFileHandle fd);
+
+ internal static bool TryGetFileSystemType(SafeFileHandle fd, out UnixFileSystemTypes fileSystemType)
+ {
+ long fstatfsResult = GetFileSystemType(fd);
+ fileSystemType = (UnixFileSystemTypes)fstatfsResult;
+ return fstatfsResult != -1;
+ }
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs
index 79edbf6adad46..24b5b9ad5a78c 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs
@@ -10,5 +10,29 @@ internal static partial class NetSecurityNative
{
[DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_IsNtlmInstalled")]
internal static extern bool IsNtlmInstalled();
+
+ [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")]
+ private static extern int EnsureGssInitialized();
+
+ static NetSecurityNative()
+ {
+ GssInitializer.Initialize();
+ }
+
+ internal static class GssInitializer
+ {
+ static GssInitializer()
+ {
+ if (EnsureGssInitialized() != 0)
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ internal static void Initialize()
+ {
+ // No-op that exists to provide a hook for other static constructors.
+ }
+ }
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs
index 338706ea8491b..d5f6d1637507f 100644
--- a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs
+++ b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs
@@ -91,5 +91,6 @@ internal static partial class Errors
internal const int ERROR_EVENTLOG_FILE_CHANGED = 0x5DF;
internal const int ERROR_TRUSTED_RELATIONSHIP_FAILURE = 0x6FD;
internal const int ERROR_RESOURCE_LANG_NOT_FOUND = 0x717;
+ internal const int ERROR_NOT_A_REPARSE_POINT = 0x1126;
}
}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs
new file mode 100644
index 0000000000000..9ecd41c46bd6b
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateSymbolicLink.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ ///
+ /// The link target is a directory.
+ ///
+ internal const int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1;
+
+ ///
+ /// Allows creation of symbolic links from a process that is not elevated. Requires Windows 10 Insiders build 14972 or later.
+ /// Developer Mode must first be enabled on the machine before this option will function.
+ ///
+ internal const int SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2;
+
+ [DllImport(Libraries.Kernel32, EntryPoint = "CreateSymbolicLinkW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
+ private static extern bool CreateSymbolicLinkPrivate(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
+
+ ///
+ /// Creates a symbolic link.
+ ///
+ /// The symbolic link to be created.
+ /// The name of the target for the symbolic link to be created.
+ /// If it has a device name associated with it, the link is treated as an absolute link; otherwise, the link is treated as a relative link.
+ /// if the link target is a directory; otherwise.
+ internal static void CreateSymbolicLink(string symlinkFileName, string targetFileName, bool isDirectory)
+ {
+ string originalPath = symlinkFileName;
+ symlinkFileName = PathInternal.EnsureExtendedPrefixIfNeeded(symlinkFileName);
+ targetFileName = PathInternal.EnsureExtendedPrefixIfNeeded(targetFileName);
+
+ int flags = 0;
+
+ bool isAtLeastWin10Build14972 =
+ Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 14972 ||
+ Environment.OSVersion.Version.Major >= 11;
+
+ if (isAtLeastWin10Build14972)
+ {
+ flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+ }
+
+ if (isDirectory)
+ {
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+ }
+
+ bool success = CreateSymbolicLinkPrivate(symlinkFileName, targetFileName, flags);
+
+ int error;
+ if (!success)
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error(originalPath);
+ }
+ // In older versions we need to check GetLastWin32Error regardless of the return value of CreateSymbolicLink,
+ // e.g: if the user doesn't have enough privileges to create a symlink the method returns success which we can consider as a silent failure.
+ else if (!isAtLeastWin10Build14972 && (error = Marshal.GetLastWin32Error()) != 0)
+ {
+ throw Win32Marshal.GetExceptionForWin32Error(error, originalPath);
+ }
+ }
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs
new file mode 100644
index 0000000000000..be8def215178f
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeviceIoControl.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ // https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point
+ internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8;
+
+ [DllImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
+ internal static extern bool DeviceIoControl(
+ SafeHandle hDevice,
+ uint dwIoControlCode,
+ IntPtr lpInBuffer,
+ uint nInBufferSize,
+ byte[] lpOutBuffer,
+ uint nOutBufferSize,
+ out uint lpBytesReturned,
+ IntPtr lpOverlapped);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs
index f2a3872299d2c..cc4896c1c52e4 100644
--- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs
@@ -9,6 +9,7 @@ internal static partial class IOReparseOptions
{
internal const uint IO_REPARSE_TAG_FILE_PLACEHOLDER = 0x80000015;
internal const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
+ internal const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C;
}
internal static partial class FileOperations
@@ -18,6 +19,7 @@ internal static partial class FileOperations
internal const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
internal const int FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;
+ internal const int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
internal const int FILE_FLAG_OVERLAPPED = 0x40000000;
internal const int FILE_LIST_DIRECTORY = 0x0001;
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs
new file mode 100644
index 0000000000000..756b1bbd72db1
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFinalPathNameByHandle.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ internal const uint FILE_NAME_NORMALIZED = 0x0;
+
+ // https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew (kernel32)
+ [DllImport(Libraries.Kernel32, EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
+ internal static unsafe extern uint GetFinalPathNameByHandle(
+ SafeFileHandle hFile,
+ char* lpszFilePath,
+ uint cchFilePath,
+ uint dwFlags);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs
new file mode 100644
index 0000000000000..3bcb9162d57bf
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.REPARSE_DATA_BUFFER.cs
@@ -0,0 +1,37 @@
+// 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 Kernel32
+ {
+ // https://docs.microsoft.com/windows-hardware/drivers/ifs/fsctl-get-reparse-point
+ internal const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
+
+ internal const uint SYMLINK_FLAG_RELATIVE = 1;
+
+ // https://msdn.microsoft.com/library/windows/hardware/ff552012.aspx
+ // We don't need all the struct fields; omitting the rest.
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct REPARSE_DATA_BUFFER
+ {
+ internal uint ReparseTag;
+ internal ushort ReparseDataLength;
+ internal ushort Reserved;
+ internal SymbolicLinkReparseBuffer ReparseBufferSymbolicLink;
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SymbolicLinkReparseBuffer
+ {
+ internal ushort SubstituteNameOffset;
+ internal ushort SubstituteNameLength;
+ internal ushort PrintNameOffset;
+ internal ushort PrintNameLength;
+ internal uint Flags;
+ }
+ }
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs
new file mode 100644
index 0000000000000..159c26438c280
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.Delegate.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ internal delegate bool ConsoleCtrlHandlerRoutine(int controlType);
+
+ [DllImport(Libraries.Kernel32, SetLastError = true)]
+ internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs
index c6ee59a77a2b9..112d5b4d5a89b 100644
--- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.Win32.SafeHandles;
-using System;
using System.Runtime.InteropServices;
internal static partial class Interop
@@ -11,10 +9,11 @@ internal static partial class Kernel32
{
internal const int CTRL_C_EVENT = 0;
internal const int CTRL_BREAK_EVENT = 1;
-
- internal delegate bool ConsoleCtrlHandlerRoutine(int controlType);
+ internal const int CTRL_CLOSE_EVENT = 2;
+ internal const int CTRL_LOGOFF_EVENT = 5;
+ internal const int CTRL_SHUTDOWN_EVENT = 6;
[DllImport(Libraries.Kernel32, SetLastError = true)]
- internal static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine handler, bool addOrRemove);
+ internal static extern unsafe bool SetConsoleCtrlHandler(delegate* unmanaged HandlerRoutine, bool Add);
}
}
diff --git a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs
index 60e7a0aa466f1..ad087304b4e5a 100644
--- a/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs
+++ b/src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs
@@ -64,19 +64,7 @@ internal static int FillAttributeInfo(string? path, ref Interop.Kernel32.WIN32_F
{
errorCode = Marshal.GetLastWin32Error();
- if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND
- && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND
- && errorCode != Interop.Errors.ERROR_NOT_READY
- && errorCode != Interop.Errors.ERROR_INVALID_NAME
- && errorCode != Interop.Errors.ERROR_BAD_PATHNAME
- && errorCode != Interop.Errors.ERROR_BAD_NETPATH
- && errorCode != Interop.Errors.ERROR_BAD_NET_NAME
- && errorCode != Interop.Errors.ERROR_INVALID_PARAMETER
- && errorCode != Interop.Errors.ERROR_NETWORK_UNREACHABLE
- && errorCode != Interop.Errors.ERROR_NETWORK_ACCESS_DENIED
- && errorCode != Interop.Errors.ERROR_INVALID_HANDLE // eg from \\.\CON
- && errorCode != Interop.Errors.ERROR_FILENAME_EXCED_RANGE // Path is too long
- )
+ if (!IsPathUnreachableError(errorCode))
{
// Assert so we can track down other cases (if any) to add to our test suite
Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION || errorCode == Interop.Errors.ERROR_SEM_TIMEOUT,
@@ -127,5 +115,27 @@ internal static int FillAttributeInfo(string? path, ref Interop.Kernel32.WIN32_F
return errorCode;
}
+
+ internal static bool IsPathUnreachableError(int errorCode)
+ {
+ switch (errorCode)
+ {
+ case Interop.Errors.ERROR_FILE_NOT_FOUND:
+ case Interop.Errors.ERROR_PATH_NOT_FOUND:
+ case Interop.Errors.ERROR_NOT_READY:
+ case Interop.Errors.ERROR_INVALID_NAME:
+ case Interop.Errors.ERROR_BAD_PATHNAME:
+ case Interop.Errors.ERROR_BAD_NETPATH:
+ case Interop.Errors.ERROR_BAD_NET_NAME:
+ case Interop.Errors.ERROR_INVALID_PARAMETER:
+ case Interop.Errors.ERROR_NETWORK_UNREACHABLE:
+ case Interop.Errors.ERROR_NETWORK_ACCESS_DENIED:
+ case Interop.Errors.ERROR_INVALID_HANDLE: // eg from \\.\CON
+ case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: // Path is too long
+ return true;
+ default:
+ return false;
+ }
+ }
}
}
diff --git a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs
index b98f271183145..148d1dc9a0700 100644
--- a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs
+++ b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs
@@ -5,10 +5,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
using System.Security.Authentication.ExtendedProtection;
namespace System.Net
{
+ [UnsupportedOSPlatform("tvos")]
internal sealed partial class NTAuthentication
{
private bool _isServer;
diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
index 98c23bdcde0c0..54497e28618f7 100644
--- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
+++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs
@@ -1950,5 +1950,23 @@ public async Task GetAsync_InvalidUrl_ExpectedExceptionThrown()
await Assert.ThrowsAsync(() => client.GetStringAsync(invalidUri));
}
}
+
+ // HttpRequestMessage ctor guards against such Uris before .NET 6. We allow passing relative/unknown Uris to BrowserHttpHandler.
+ public static bool InvalidRequestUriTest_IsSupported => PlatformDetection.IsNotNetFramework && PlatformDetection.IsNotBrowser;
+
+ [ConditionalFact(nameof(InvalidRequestUriTest_IsSupported))]
+ public async Task SendAsync_InvalidRequestUri_Throws()
+ {
+ using var invoker = new HttpMessageInvoker(CreateHttpClientHandler());
+
+ var request = new HttpRequestMessage(HttpMethod.Get, (Uri)null);
+ await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None));
+
+ request = new HttpRequestMessage(HttpMethod.Get, new Uri("/relative", UriKind.Relative));
+ await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None));
+
+ request = new HttpRequestMessage(HttpMethod.Get, new Uri("foo://foo.bar"));
+ await Assert.ThrowsAsync(() => invoker.SendAsync(request, CancellationToken.None));
+ }
}
}
diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs
index 02ffa607c94aa..a45aab12165a5 100644
--- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs
@@ -127,5 +127,63 @@ private string GenerateTestFileName(int? index, string memberName, int lineNumbe
lineNumber,
index.GetValueOrDefault(),
Guid.NewGuid().ToString("N").Substring(0, 8)); // randomness to avoid collisions between derived test classes using same base method concurrently
+
+ ///
+ /// In some cases (such as when running without elevated privileges),
+ /// the symbolic link may fail to create. Only run this test if it creates
+ /// links successfully.
+ ///
+ protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value;
+
+ private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() =>
+ {
+ bool success = true;
+
+ // Verify file symlink creation
+ string path = Path.GetTempFileName();
+ string linkPath = path + ".link";
+ success = CreateSymLink(path, linkPath, isDirectory: false);
+ try { File.Delete(path); } catch { }
+ try { File.Delete(linkPath); } catch { }
+
+ // Verify directory symlink creation
+ path = Path.GetTempFileName();
+ linkPath = path + ".link";
+ success = success && CreateSymLink(path, linkPath, isDirectory: true);
+ try { Directory.Delete(path); } catch { }
+ try { Directory.Delete(linkPath); } catch { }
+
+ return success;
+ });
+
+ protected static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory)
+ {
+#if NETFRAMEWORK
+ bool isWindows = true;
+#else
+ if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsBrowser()) // OSes that don't support Process.Start()
+ {
+ return false;
+ }
+ bool isWindows = OperatingSystem.IsWindows();
+#endif
+ Process symLinkProcess = new Process();
+ if (isWindows)
+ {
+ symLinkProcess.StartInfo.FileName = "cmd";
+ symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath));
+ }
+ else
+ {
+ symLinkProcess.StartInfo.FileName = "/bin/ln";
+ symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath));
+ }
+ symLinkProcess.StartInfo.RedirectStandardOutput = true;
+ symLinkProcess.Start();
+
+ symLinkProcess.WaitForExit();
+ return (0 == symLinkProcess.ExitCode);
+ }
+
}
}
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
index fc829692c03f4..90f21d02f5c49 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.IO;
+using System.Linq.Expressions;
using System.Security;
using System.Security.Authentication;
using System.Reflection;
@@ -36,7 +37,8 @@ public static partial class PlatformDetection
public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"));
public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
public static bool IsNotBrowser => !IsBrowser;
- public static bool IsNotMobile => IsNotBrowser && !IsMacCatalyst && !IsiOS && !IstvOS && !IsAndroid;
+ public static bool IsMobile => IsBrowser || IsMacCatalyst || IsiOS || IstvOS || IsAndroid;
+ public static bool IsNotMobile => !IsMobile;
public static bool IsNotNetFramework => !IsNetFramework;
public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm;
@@ -66,6 +68,22 @@ public static partial class PlatformDetection
public static bool IsUsingLimitedCultures => !IsNotMobile;
public static bool IsNotUsingLimitedCultures => IsNotMobile;
+ public static bool IsLinqExpressionsBuiltWithIsInterpretingOnly => s_LinqExpressionsBuiltWithIsInterpretingOnly.Value;
+ public static bool IsNotLinqExpressionsBuiltWithIsInterpretingOnly => !IsLinqExpressionsBuiltWithIsInterpretingOnly;
+ private static readonly Lazy s_LinqExpressionsBuiltWithIsInterpretingOnly = new Lazy(GetLinqExpressionsBuiltWithIsInterpretingOnly);
+ private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly()
+ {
+ Type type = typeof(LambdaExpression);
+ if (type != null)
+ {
+ // The "Accept" method is under FEATURE_COMPILE conditional so it should not exist
+ MethodInfo methodInfo = type.GetMethod("Accept", BindingFlags.NonPublic | BindingFlags.Static);
+ return methodInfo == null;
+ }
+
+ return false;
+ }
+
// Please make sure that you have the libgdiplus dependency installed.
// For details, see https://docs.microsoft.com/dotnet/core/install/dependencies?pivots=os-macos&tabs=netcore31#libgdiplus
public static bool IsDrawingSupported
@@ -225,14 +243,9 @@ public static string GetDistroVersionString()
private static bool GetStaticNonPublicBooleanPropertyValue(string typeName, string propertyName)
{
- Type globalizationMode = Type.GetType(typeName);
- if (globalizationMode != null)
+ if (Type.GetType(typeName)?.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod is MethodInfo mi)
{
- MethodInfo methodInfo = globalizationMode.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod;
- if (methodInfo != null)
- {
- return (bool)methodInfo.Invoke(null, null);
- }
+ return (bool)mi.Invoke(null, null);
}
return false;
@@ -273,6 +286,10 @@ private static Version GetICUVersion()
public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value;
+ private static readonly Lazy s_fileLockingDisabled = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("Microsoft.Win32.SafeHandles.SafeFileHandle", "DisableFileLocking"));
+
+ public static bool IsFileLockingEnabled => IsWindows || !s_fileLockingDisabled.Value;
+
private static bool GetIsInContainer()
{
if (IsWindows)
diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs
index ee124f31be8ef..0237e8ad096b7 100644
--- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs
+++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Threading;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
@@ -37,6 +38,8 @@ public override Func RealizeService(ServiceC
catch (Exception ex)
{
DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex);
+
+ Debug.Fail($"We should never get exceptions from the background compilation.{Environment.NewLine}{ex}");
}
},
null);
diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
index b93a11d956619..afe118acf7909 100644
--- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
+++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
@@ -32,11 +32,12 @@ public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDispo
internal ServiceProvider(IEnumerable serviceDescriptors, ServiceProviderOptions options)
{
+ // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root
+ Root = new ServiceProviderEngineScope(this, isRootScope: true);
_engine = GetEngine();
_createServiceAccessor = CreateServiceAccessor;
_realizedServices = new ConcurrentDictionary>();
- Root = new ServiceProviderEngineScope(this, isRootScope: true);
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
// The list of built in services that aren't part of the list of service descriptors
// keep this in sync with CallSiteFactory.IsService
diff --git a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj
index db9472c1432f3..f22780b1a5e6e 100644
--- a/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj
+++ b/src/libraries/Microsoft.IO.Redist/src/Microsoft.IO.Redist.csproj
@@ -1,4 +1,4 @@
-
+
net472
$(DefineConstants);MS_IO_REDIST
@@ -34,6 +34,8 @@
Link="Microsoft\IO\File.cs" />
+
+
+
+
+
The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size.
+
+ The link's file system entry type is inconsistent with that of its target: {0}
+
Could not find a part of the path.
diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs
index 5437590241a62..38b21a9eb11cc 100644
--- a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs
+++ b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs
@@ -94,7 +94,7 @@ public static IEnumerable